iOS知识点总结

2023-04-12  本文已影响0人  烟影很美

多线程:

进程和线程的区别:进程是火车, 线程是车厢。进程间不共享上下文, 同一进程下的线程共享进程的上下文
• 同步(sync):阻塞当前线程, 任务立即执行
• 异步(async):可以在新的线程中执行任务, 具备开启新线程的能力。
• 串行:同一时间只有一个任务执行
• 并行:同一时间有多个任务执行(多核)
• 并发:单核通过时间片轮转宏观上实现并行的机制


数据结构:

• 堆:逻辑结构完全二叉树, 可以用一维数组实现, 这种数据结构方便求极值。向上调整算法/向下调整算法
• 栈:先进后出, 可以用一维数组或者链表实现

内存:

• 堆:动态分配, 主要为了解决栈空间释放后变量也被释放的问题
• 栈:逻辑上先进后出, 由系统控制。


Swift:

函数调用方式:

静态, 动态。动态包括函数表调用和消息发送。扩展(extension)没有函数表, 所以只能使用静态或者消息发送的方式。结构体及其扩展静态调用;类默认函数表调用, final-静态, @objc-函数表, dynamic-消息发送;类扩展默认使用静态调用, 类扩展+@objc/dynamic-消息发送

Swift与OC区别:
  1. Swift是静态语言, 有类型推断, OC是动态语言
  2. Swift面向协议编程, OC面向对象编程
  3. Swift注重值类型, OC注重引用类型
  4. Swift支持泛型, OC只支持轻量泛型
  5. Swift支持静态派发(效率高)、动态派发(函数表派发、消息派发), OC支持动态派发(消息派发)
  6. Swift支持函数式编程
  7. Swift的协议不仅可以被类实现, 也可以被structenum实现
  8. Swift有元组类型、支持运算法重载
  9. Swift支持命名空间
  10. Swift支持默认参数(函数)
  11. Swift比OC代码更简洁
  12. 值类型优化:如写时拷贝

Block:

全局block在数据区, 没有捕获变量的block原本应该在栈, 但是编译器优化, 转移到了数据区。捕获变量的block变量会在堆, 但如果是block对象, 因为没有变量名, 所以不会再其他地方调用, 会在栈。捕获变量的block对象如果对其使用copy, 会在堆。(MRC下, 如果不对捕获变量的block使用COPY, 就在栈)


layoutSubviews调用时机:

有尺寸且添加到父View上时, 父视图是ScrollView且滚动时, 改变尺寸时


load、initialize方法的区别:

• 调用方式

  1. load是根据函数地址直接调用
  2. initialize是通过objc_msgSend调用

• 调用时刻

  1. load是runtime加载类、分类的时候调用(只会调用1次)
  2. initialize是类第一次接收到消息的时候调用, 每一个类只会initialize一次,

如果有SubClass继承Class.
则在SubClass第一次收到消息时, 会先调用SubClass-initialize, 再调用Class-initialize;
如果SubClass没有重写initialize, 则在SubClass-initialize过程中触发Class-initialize, 表现为Class-initialize被调用了两次. 即一个类向上的继承树上有N个类, 在接收到消息时就会触发N+1initialize, 如果某个类没有重写initialize, 则会向上调用最近的initialize
综上: 重写initialize时需用考虑使用dispatchonce避免重复调用和初始化.


热启动与冷启动

参考: http://events.jianshu.io/p/783d8b3ef884https://www.jianshu.com/p/4c1adabc8b90

  1. 热启动:就是按下home键的时候, app还存在一段时间, 这时点击app马上就能恢复到原状态, 这种启动我们称为热启动。当APP 启动时需要的 dylibs 仍然停留在设备的磁盘缓存的时候,这个时候就是热启动,热启动的速度会更快。
  2. 冷启动:app被kill掉之后, 重新打开启动过程为冷启动。

性能优化:


冷启动:

冷启动可大致分为两个阶段, 分别为main函数之前、main之后到didFinishLaunching执行完毕, 很多时候, 首页的完成展示前的阶段可包含在启动阶段内. 上述两个阶段为pre-mainmain.

pre-main阶段:
pre-main的过程
  1. 加载mach-o可执行文件
  2. 加载dyld到App进程,
  3. 加载动态库(包括所依赖的所有动态库)
  4. Rebase 内部指针调整。
  5. Bind 把指针正确的指向Image外部的内容。通过符号表查找
  6. 初始化Objective-C Runtime, Objc运行时的初始化处理, 包括Objc相关类的注册、category注册、selector唯一性检查
  7. 其他初始化, 包括执行了+load()方法、attribute((constructor))修饰的函数调用、创建C++静态全局变量
针对pre-main的优化:
  1. 尽量缩小mach-o的大小(待研究) - 1
  2. 减少动态库, 在动态库较多时将多个动态库合并 - 3
  3. 减少Objc类, category的数量, 清除用不到的类、函数 - 4, 6
  4. 使用dispatchonce+initialize代替+load, 尽量减少attribute((constructor))的调用, 也可以考虑用dispatchonce+initialize代替 - 7
  5. 二进制重排.
t1.png
main阶段:
  1. dyld调 main()
  2. 调UIApplicationMain()
  3. 调applicationWillFinishLaunching
  4. 调didFinishLaunchingWithOptions

这部分包含大量APP配置和业务代码, 梳理好其内容, 将能延后的内容尽量延后, 此阶段可用的办法有:

  1. 延后部分代码执行
  2. 使用闪屏优化体验
  3. 内嵌启动首页展示数据
  4. 多线程

热启动

  1. 数据优化,将耗时操作做异步处理。
  2. 检查NSUserDefaults的存储,NSUserDefaults实际上是在Library文件夹下会生产一个plist文件,加载的时候是整个plist配置文件全部load到内存中。所以非常频繁的存取大量数据也是有可能导致APP启动卡顿的

卡顿

参考 https://zhuanlan.zhihu.com/p/502990984

手机成像过程

产生卡顿是由于屏幕的成像显示导致, 而屏幕画面的显示离不开手机的CPU和GPU. CPU主要负责计算, 比如说界面的布局排版, 文字的大小和颜色, 图片显示的解码等操作都需要CPU计算, CPU将计算好的数据提交给GPU, GPU就将CPU计算好的数据进行渲染。因为屏幕显示的数据是特定格式的,只有通过GPU渲染过的数据才能显示在屏幕上;GPU把渲染过的数据放到帧缓存区, 视频控制器从帧缓存区读取数据显示到屏幕上.


卡顿.png
卡顿的主要原因

CPU和GPU所花费的时间太长从而造成俗称的丢帧(掉帧). 垂直同步信号的发射频率是固定的, 信号发出, 表明着即将显示数据。若是期间, CPU或GPU有一步耗时较长(第3帧的渲染), 垂直信号已发出, 可是GPU尚未渲染完, 那么就是显示渲染好的第2帧数据, 连续显示相同的帧, 就形成了画面卡顿; 第3帧会在第4次同步信号过来时再显示。这就是卡顿的根本原因.

1. CPU和GPU

CUP(Central processing Unit,中央处理器)
对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制(Core Graphics)

GPU (Graphics Processing Unit,图形处理器)
纹理的渲染

优化:
2. 离屏渲染(尽量避免出现离屏渲染)

在OpenGL中,GPU有2种渲染方式
On-Screen Rendering:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作
Off-Screen Rendering:离屏渲染,在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作

离屏渲染消耗性能的原因

1、需要创建新的缓冲区
2、离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕

哪些操作会触发离屏渲染?

光栅化,layer.shouldRasterize = YES
遮罩,layer.mask
? 圆角,同时设置layer.masksToBounds = YES、layer.cornerRadius大于0(考虑通过CoreGraphics绘制裁剪圆角,或者叫UI提供圆角图片)
阴影,layer.shadowXXX (如果设置了layer.shadowPath就不会产生离屏渲染)。


耗电

  1. 尽可能降低CPU、GPU功耗
  2. 少用定时器
  3. 优化I/O操作
  4. 尽量不要频繁写入小数据,最好批量一次性写入
  5. 读写大量重要数据时,考虑用dispatch_io,其提供了基于GCD的异步操作文件I/O的API。用dispatch_io系统会优化磁盘访问
  6. 数据量比较大的,建议使用数据库(比如SQLite、CoreData)

网络

  1. 减少、压缩网络数据
  2. 如果多次请求的结果是相同的,尽量使用缓存
  3. 使用断点续传,否则网络不稳定时可能多次传输相同的内容
  4. 网络不可用时,不要尝试执行网络请求
  5. 让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间
  6. 批量传输,比如,下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块地下载。如果下载广告,一 次性多下载一些,然后再慢慢展示。如果下载电子邮件,一次下载多封,不要一封一封地下载

定位

  1. 如果只是需要快速确定用户位置,最好用CLLocationManager的requestLocation方法。定位完成后,会自动让定位硬件断电
  2. 如果不是导航应用,尽量不要实时更新位置,定位完毕就关掉定位服务
  3. 尽量降低定位精度,比如尽量不要使用精度最高的kCLLocationAccuracyBest
  4. 需要后台定位时,尽量设置pausesLocationUpdatesAutomatically为YES,如果用户不太可能移动的时候系统会自动暂停位置更新
  5. 尽量不要使用startMonitoringSignificantLocationChanges,优先考虑startMonitoringForRegion:
  6. 用户移动、摇晃、倾斜设备时,会产生动作(motion)事件,这些事件由加速度计、陀螺仪、磁力计等硬件检测。在不需要检测的场合,应该及时关闭这些硬件
上一篇下一篇

猜你喜欢

热点阅读