iOS知识点总结
多线程:
进程和线程的区别:进程是火车, 线程是车厢。进程间不共享上下文, 同一进程下的线程共享进程的上下文
• 同步(sync):阻塞当前线程, 任务立即执行
• 异步(async):可以在新的线程中执行任务, 具备开启新线程的能力。
• 串行:同一时间只有一个任务执行
• 并行:同一时间有多个任务执行(多核)
• 并发:单核通过时间片轮转宏观上实现并行的机制
数据结构:
• 堆:逻辑结构完全二叉树, 可以用一维数组实现, 这种数据结构方便求极值。向上调整算法/向下调整算法
• 栈:先进后出, 可以用一维数组或者链表实现
内存:
• 堆:动态分配, 主要为了解决栈空间释放后变量也被释放的问题
• 栈:逻辑上先进后出, 由系统控制。
Swift
:
函数调用方式:
静态, 动态。动态包括函数表调用和消息发送。扩展(extension)没有函数表, 所以只能使用静态或者消息发送的方式。结构体及其扩展静态调用;类默认函数表调用, final-静态, @objc-函数表, dynamic-消息发送;类扩展默认使用静态调用, 类扩展+@objc/dynamic-消息发送
Swift
与OC区别:
-
Swift
是静态语言, 有类型推断, OC是动态语言 -
Swift
面向协议编程, OC面向对象编程 -
Swift
注重值类型, OC注重引用类型 -
Swift
支持泛型, OC只支持轻量泛型 -
Swift
支持静态派发(效率高)、动态派发(函数表派发、消息派发), OC支持动态派发(消息派发) -
Swift
支持函数式编程 -
Swift
的协议不仅可以被类实现, 也可以被struct
和enum
实现 -
Swift
有元组类型、支持运算法重载 -
Swift
支持命名空间 -
Swift
支持默认参数(函数) -
Swift
比OC代码更简洁 - 值类型优化:如写时拷贝
Block:
全局block在数据区, 没有捕获变量的block原本应该在栈, 但是编译器优化, 转移到了数据区。捕获变量的block变量会在堆, 但如果是block对象, 因为没有变量名, 所以不会再其他地方调用, 会在栈。捕获变量的block对象如果对其使用copy, 会在堆。(MRC下, 如果不对捕获变量的block使用COPY, 就在栈)
layoutSubviews调用时机:
有尺寸且添加到父View上时, 父视图是ScrollView且滚动时, 改变尺寸时
load、initialize方法的区别:
• 调用方式
- load是根据函数地址直接调用
- initialize是通过objc_msgSend调用
• 调用时刻
- load是runtime加载类、分类的时候调用(只会调用1次)
- initialize是类第一次接收到消息的时候调用, 每一个类只会initialize一次,
如果有
SubClass
继承Class
.
则在SubClass
第一次收到消息时, 会先调用SubClass-initialize
, 再调用Class-initialize
;
如果SubClass
没有重写initialize
, 则在SubClass-initialize
过程中触发Class-initialize
, 表现为Class-initialize
被调用了两次. 即一个类向上的继承树上有N
个类, 在接收到消息时就会触发N+1
次initialize
, 如果某个类没有重写initialize
, 则会向上调用最近的initialize
综上: 重写initialize
时需用考虑使用dispatchonce
避免重复调用和初始化.
热启动与冷启动
参考: http://events.jianshu.io/p/783d8b3ef884 、 https://www.jianshu.com/p/4c1adabc8b90
- 热启动:就是按下home键的时候, app还存在一段时间, 这时点击app马上就能恢复到原状态, 这种启动我们称为热启动。当APP 启动时需要的 dylibs 仍然停留在设备的磁盘缓存的时候,这个时候就是热启动,热启动的速度会更快。
- 冷启动:app被kill掉之后, 重新打开启动过程为冷启动。
性能优化:
冷启动:
冷启动可大致分为两个阶段, 分别为main
函数之前、main
之后到didFinishLaunching
执行完毕, 很多时候, 首页的完成展示前的阶段可包含在启动阶段内. 上述两个阶段为pre-main
和main
.
pre-main阶段:
pre-main的过程
- 加载
mach-o
可执行文件 - 加载dyld到App进程,
- 加载动态库(包括所依赖的所有动态库)
- Rebase 内部指针调整。
- Bind 把指针正确的指向Image外部的内容。通过符号表查找
- 初始化Objective-C Runtime, Objc运行时的初始化处理, 包括Objc相关类的注册、category注册、selector唯一性检查
- 其他初始化, 包括执行了+load()方法、attribute((constructor))修饰的函数调用、创建C++静态全局变量
针对pre-main的优化:
- 尽量缩小
mach-o
的大小(待研究) - 1 - 减少动态库, 在动态库较多时将多个动态库合并 - 3
- 减少Objc类, category的数量, 清除用不到的类、函数 - 4, 6
- 使用
dispatchonce
+initialize
代替+load
, 尽量减少attribute((constructor))
的调用, 也可以考虑用dispatchonce
+initialize
代替 - 7 - 二进制重排.
main阶段:
- dyld调 main()
- 调UIApplicationMain()
- 调applicationWillFinishLaunching
- 调didFinishLaunchingWithOptions
这部分包含大量APP配置和业务代码, 梳理好其内容, 将能延后的内容尽量延后, 此阶段可用的办法有:
- 延后部分代码执行
- 使用闪屏优化体验
- 内嵌启动首页展示数据
- 多线程
热启动
- 数据优化,将耗时操作做异步处理。
- 检查NSUserDefaults的存储,NSUserDefaults实际上是在Library文件夹下会生产一个plist文件,加载的时候是整个plist配置文件全部load到内存中。所以非常频繁的存取大量数据也是有可能导致APP启动卡顿的
卡顿
手机成像过程
产生卡顿是由于屏幕的成像显示导致, 而屏幕画面的显示离不开手机的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,图形处理器)
纹理的渲染
优化:
- 尽可能减少CPU、GPU资源消耗
- 尽量用轻量级的对象,比如用不到事件处理的地方,可以考虑使用CALayer取代UIView
- 不要频繁地调用UIView的相关属性,比如frame、bounds、transform等属性,尽量减少不必要的修改
- 尽量提前计算好布局,在有需要时一次性调整对应的属性,不要多次修改属性
- Autolayout会比直接设置frame消耗更多的CPU资源
- 图片的size最好刚好跟UIImageView的size保持一致
- 控制一下线程的最大并发数量
- 尽量避免短时间内大量图片的显示,尽可能将多张图片合成一张进行显示
- GPU能处理的最大纹理尺寸是4096x4096,一旦超过这个尺寸,就会占用CPU资源进行处理,所以纹理尽量不要超过这个尺寸
- 尽量减少视图数量和层次
- 减少透明的视图(alpha<1),不透明的就设置opaque为YES
- 尽量把耗时的操作放到子线程(文本尺寸、图片处理)
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就不会产生离屏渲染)。
耗电
- 尽可能降低CPU、GPU功耗
- 少用定时器
- 优化I/O操作
- 尽量不要频繁写入小数据,最好批量一次性写入
- 读写大量重要数据时,考虑用dispatch_io,其提供了基于GCD的异步操作文件I/O的API。用dispatch_io系统会优化磁盘访问
- 数据量比较大的,建议使用数据库(比如SQLite、CoreData)
网络
- 减少、压缩网络数据
- 如果多次请求的结果是相同的,尽量使用缓存
- 使用断点续传,否则网络不稳定时可能多次传输相同的内容
- 网络不可用时,不要尝试执行网络请求
- 让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间
- 批量传输,比如,下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块地下载。如果下载广告,一 次性多下载一些,然后再慢慢展示。如果下载电子邮件,一次下载多封,不要一封一封地下载
定位
- 如果只是需要快速确定用户位置,最好用
CLLocationManager的requestLocation
方法。定位完成后,会自动让定位硬件断电 - 如果不是导航应用,尽量不要实时更新位置,定位完毕就关掉定位服务
- 尽量降低定位精度,比如尽量不要使用精度最高的
kCLLocationAccuracyBest
- 需要后台定位时,尽量设置
pausesLocationUpdatesAutomatically
为YES,如果用户不太可能移动的时候系统会自动暂停位置更新 - 尽量不要使用
startMonitoringSignificantLocationChanges
,优先考虑startMonitoringForRegion:
- 用户移动、摇晃、倾斜设备时,会产生动作(
motion
)事件,这些事件由加速度计、陀螺仪、磁力计等硬件检测。在不需要检测的场合,应该及时关闭这些硬件