性能之内存管理
2017-06-03 本文已影响0人
清水一心
应用中的内存消耗分为:栈大小和堆大小。
栈大小
- 可被递归调用的最大方法数
- 一个方法中最多可以使用的变量个数
- 视图层级中可以嵌入的最大视图深度
堆大小
每个进程的所有线程共享一个堆。一个应用可以使用的堆大小通常远远小于设备的RAM值。
自动释放池块(autoreleasepool)
在一些特殊情况下,需要创建自己的autoreleasepool。
* 当你有一个创建了很多临时对象的循环时
* 当你创建一个线程时
ARC的规则
* 不能实现或调用retain、release、autorelease或retainCount方法
这一规则不仅针对对象,对选择器同样有效
* 可以实现dealloc方法,但不能调用它包括超类
* 不能调用NSAllocateObject和NSDeallocateObject方法
应该使用alloc方法创建对象,运行时负责回收对象
* 不能在C语言的结构体内使用对象指针
* 不能在 id 类型和void * 类型之间自动转换。如果需要,必须做显示转换。
* 不能使用NSAutoreleasepool,要替换成autoreleasepool
* 不能使用NSZone内寸区域
* 属性的访问器名称不能以new开头,以确保与MRC的互操作性
* Core Foundation类型需要自己手动管理内存
引用类型
- 弱引用:不会增加引用计数
- 强引用:会增加引用计数
变量限定符
- _strong、 _weak 、_unsafe_unretained、_autoreleasesing
例子:
Person* _strong p1 = [[Person alloc] init]// 引用计数为1,并且对象在p1引用期间不会被回收
Person* _weak p2 = [[Person alloc] init]// 引用计数为0,对象会被立即释放,且p2将被设置为nil
Person* _unsafe_unretained p3 = [[Person alloc] init]// 引用计数为1,对象会被立即释放,且p3不会被设置为nil
Person*_autoreleasing p4 = [[Person alloc] init]// 引用计数为1,当方法返回时对象会被立即释放
属性限定符
这个就不在此做解释了,不懂的,百度上有很多教程
僵尸对象
用来捕捉内存错误的调试功能
通常情况下,当引用计数为0时,对象会立即释放,会使得调试困难。开启僵尸对象,那么对象不会立即释放内存,而是标记为僵尸。
NSZombieEnabled是一个环境变量,可以控制Core Foundation的运行时是否将使用僵尸对象
注意:在发布构建时一定要禁用
内存管理规则
* 你拥有所有自己创建的对象,如new、alloc、copy、mutableCopy
* 你可以用MRC中的retain或ARC中的__strong引用来拥有任何对象的持有关系
* 一定不能抛弃原本并不存在持有关系的对象
循环引用
主要出现的场景委托、块、线程与计时器
观察者
- 键-值观察
它不会维持观察对象、被观察对象以及上下文对象的强引用。如有必要,你需要自行维护对它们的强引用。 - 通知中心
一个对象可以注册为通知中心的观察者,并接收NSNotification对象。它不会对观察者持有强引用。
返回错误
当某个方法接收NSError**参数,并在发生错误时填充错误变量,则必须使用__autoreleasing限定符。
弱类型:id
在使用常规命名的方式时,应避免使用id。尽量使用具体的类取而代之。
合理使用全局变量与单例
推荐做法
避免大量的单例
对子对象使用__strong
对父对象使用__weak
对使引用图闭合的对象(如委托)使用__weak
对数值属性,使用assign
对块属性,使用copy限定符
当声明使用NSError**参数的方法时,需要使用__autoreleasing。并注意用正确的语法: NSError*__autoreleasing *。
避免在块内直接引用外部的变量。在外用weakify,在内strongify
销毁计时器、移除观察者、解除回调(例如:委托设置为nil)
扩展
依赖注入(Typhoon和Objection框架)
Instruments进行内存分析