内存管理
29、理解引用计数
以后可以整理引用技术和垃圾回收器的区别。
1》引用计数机制通过可以递增的技术器俩管理内内存。 对象创建好了之后,其保留计数至少为1. 若保留计数为正,则对象继续存活。 当保留计数降为0时, 对象就被销毁了。
2》在对象生命周期中,其余对象通过引用来保留或释放此对象。保留与释放操作分别递增及递减保留计数
引用计数的工作原理
在引用计数架构下,对象有个计数器 [用来表示当前有多少个事务想令次对象继续存活下去] —— 引用计数
操作:
- retain 递增引用计数
- release 递减引用计数
autorelease 待稍后清理“自动释放池”时,再递减引用计数。
引用计数的一个基本简单过程
属性存取方法中的内存管理
对象之间通过属性进行引用保留新的值并释放旧值, 然后更新实例变量,令其指向新值。
Note: 这个顺序很重要:假如还未保留新值就先把旧的值释放了,而且两个值有指向同一个对象,那么,先执行release操作就可能导致系统将次对象永久回收。 而手续的retain操作则无法令这个已经测地回收的对象复生,于是实例变量就成了野指针。
自动释放池
autorelease 稍后释放, 通常是在下一次“事件循环(event loop)”时递减。 也就是延后释放了。
作用: 尤其是在方法中返回对象时更应该用它。我们并不总是想令方法调用者手工保留其值。
- (void)stringValue {
NSString *str = [[NSString alloc] initWithFormat:@"nihaoya :%d",1];
return str;
}
MRC 上要自己添加,ARC自动添加进去
- 此时返回的str对象其保留计数比期望值要多1(+1 retain count ),因为调用alloc会令保留计数+1, 而有没有预知对应的释放操作。 保留计数多1,必须设法抵消。
- 不能够在方法内释放str,否则还没等方法返回,系统吧该对象回收了。 这里应该使用autorelease,它会在稍后释放对象, 从而给调用者留下足够场的时间,使其可以在需要时保留返回值。 【次方法可以保证对象在阔月“方法调用边界”后一定存活。实际上,释放操作会在清空外层的自动释放池时候执行,除非你有自己的自动释放池,否则这个实际指的就是当前线程的下一次时间循环。】
3)由于返回str对象将稍后自动给释放, 所以多出来的那一次保留操作自然就会抵消, 无需再执行内存管理操作。 如果要持有此对象,必须要保留,retain的操作。
PS: 返回值里面使用了autorelease,延迟释放。不用再走引用计数了。 【可以看看源码分析】
保留环
循环引用,其实引用计数不会降为0。一般用“弱引用”来解决。
以ARC简化引用计数
1》在ARC之后,程序员无需担心内存管理问题了,使用ARC来编程,可省去类中的许多“样板代码”
2》ARC管理对象声明期的办法基本上就是: 在合适的地方插入“retain”以“release”操作。在ARC环境下,变量的内存管理寓意可以通过修饰符指明,而原来则需要手工执行retain 以及release操作
3》由方法锁返回的对象,其内存管理语义总是通过方法名来体现。 ARC将次确定为开发者必须遵守的规则
4》ARC只负责管理OC对象的内存。尤其要注意:CoreFoundation兑现不归ARC管理,开发者必须适时掉员工CFRetain/CFRelease
clang 编译器中带有一个“static asnlyzer”静态分析器,指明内存是否泄漏,即为检查引用计数。
ARC中引用计数还是要执行的,只不过是自动添加进入了代码。
retain / release / autorelease / dealloc
ARC在调用这个方法的时候,并不是通过普通的OC消息派发机制,而是直接调用其底层的C语言版本。
原因: 因为保留以及释放操作需要频繁执行,所以直接调用底层函数能够节省很多CPU周期。
使用ARC时必须遵循方法命名规则
alloc/new/copy/mutableCopy 这个开头的,则其返回的对象归调用者所有。
归调用者所有的意思是
:调用上述四种方法的那段代码要负责释放方法所返回的对象。 也就是说:这些对象的保留计数是正值, 而调用了这四种方法的那段代码要将其中一次保留操作抵消掉。 如果还有其他对象保留次对象,并对其调用了autorelease, 那么保留计数的值可能比1大,这也是retainCount方法不太有用的原因之一。
变量的内存管理语义
ARC也会处理局部变量与实例变量的内存管理。
默认情况下:每个变量都是指向对象的强引用。
因为对于某些代码来说,其语义和手动管理引用计数时不同。
__strong : 默认语义,保留此值
__unsafe_unretained: 不保留此值, 这么做可能不安全,因为等到再次使用变量时,其对象可能已经回收了。
__weak: 不保留此值, 但是变量可以安全使用,因为如果系统把这个对象回收了,那么变量会自动清空
__autoreleaseing: 把对象“按引用传递”(pass by reference) 给方法时,使用这个特殊的修饰符。 此值在方法返回时自动释放。
常常用weak 修饰来打破保留环。
ARC 如何清理实例变量
ARC借用objective-C++的一项特性来生成清理例程。 回收objective-c++对象时, 待回收的对象会调用所有C++对象的析构函数。编译器如果发现某个对象里含有C++对象,就会生成名为.cxx_desctruct 的方法。 而ARC则借助次特性,在该方法中生成清理内存所需要的代码。
CoreFoundation中的对象malloc出来的还是需要手动释放的。
覆写内存管理方法
ARC中不可以复写retain、release、autorelease方法。
31 在dealloc方法汇总只释放应用并解除监听
1》dealoc 方法里, 应该做的事情就是释放指向其他对象的引用,并取消原来kvo、通知, 不要做其他事情
2》如果对象持有文件描述符等系统资源,那么应该专门编写一个方法来释放此种资源。这样的类要和其他使用者约定:用完资源后必须调用close方法
3》执行异步任务的方法不应在dealloc里调用;只能在正常装下执行的那些方法不应该再dealloc里调用,因为次时对象已处于正在回收的状态了。
当一个引用计数为0的时候,就会调用dealloc方法。
dealloc 不需要手动去调用,系统会在合适的位置释放。
dealloc 方法中做些什么呢?
主要就是释放对象所拥有的引用, 也就是把所有OC对象都释放掉, ARC会通过自动生成的.cxx_desctruct 方法,在dealloc中为你自动添加这些释放代码。 对象锁拥有的其他非OC对象也要释放。 CoreFoundation的对象,要在这个方法里面手动调用释放。
虽说应该与dealloc中释放引用,但是开销较大或系统内稀缺的资源则不再此列。 像是文件描述符(file descriptor)、套接字(socket) 、大块内存等,都属于这种资源。
32 编写“异常安全代码”时留意内存管理问题
1》 捕获异常时:一定要注意将try块内锁创立的对象清理干净
2》默认情况下,ARC不生成安全处理异常所需的清理代码。开启编译器标志后,可生成这种diamante,不过会导致应用程序变大,而且会降低运行效率。
C++ 与OC的异常相互兼容,也就是说, 从其中一门语言里抛出的异常能用另外一门语言所编的“异常处理程序”(exception handler)来捕获。
OC 的错误模型表明, 异常只应在发生严重错误后抛出,有时候需要编写代码捕获并处理异常。 eg: 第三方库的错误的时候可以使用异常处理程序 。
ARC 环境下等同于上面MRC的写法
33 、用弱引用避免引用循环
1》 将某些引用设为weak,可避免出现循环引用
2》weak引用可以自动清空,也可以不自动清空。 自动清空(autonilling) 是随着ARC而引入的新特性,由运行期系统来实现。 在具备自动清空功能能的弱引用上,可以随意读取其数据,因为这种引用不会指向已经回收过的对象。
34、以“自动释放池块”降低内存峰值
1》自动释放池排布在栈中,对象收到autorelease消息后,系统将其收入最顶端的池里
2》合理运用自动释放池,可降低应用程序的内存峰值
3》@autoreleasepool 这种新式写法能够创建出更为轻便的自动释放池。
一项特性:自动释放池
释放对象有两种方式:
1》调用release方法, 使用其保留计数立即递减;
2》调用autorelease 方法,将其加入“自动释放池”中。
自动释放池:用于存放哪些需要在稍后某个时刻释放的对象。 清空(drain)自动释放池时,系统会向其中的对象发送release消息。
35、 用“僵尸对象”调试内存管理问题
1》 系统在回收对象时,可以不讲其真的回收,而是把它转化为僵尸对象。 通过环境变量NSZombieEnabled可开启此功能
2》系统会修改对象的isa指针,令其指向特殊的僵尸类,从而使该对象变为僵尸对象。僵尸类能够响应所有的选择子,响应方式为:打印一条包含消息内容及其接受者的消息,然后终止应用程序。
什么是僵尸对象?
野指针、僵尸对象
xcode上的操作cocoa 提供了“僵对象”这个功能,可以启动调试,运行期系统会把所有已回收的实例转化成特殊的“僵尸对象”, 而不会真正回收它们。 这种对象所在的核心内存无法重用,因此不可能遭到覆写。 僵尸对象收到消息后,会抛出异常,其中准确说明了发送过来的消息,并描述了回收之前的那个对象。 僵尸对象是调试内存管理问题的最佳方式。
僵尸对象 工作原理:
实现代码深植于OC的运行期程序库、Foundation框架以及CoreFoundation框架中。 系统在即将回收对象时,如果发现通过环境变量启用了僵尸对象功能能,那么还将执行一个附加步骤。 这一步骤就是吧对象转化为僵尸对象,而不彻底回收。
36、不要使用retainCount
1》对象的保留计数看似有用,实则不然,因为任何给定时间点上的“绝对保留计数”都无法反映对象生命期的全貌。
2》引入ARC之后,retainCount方法就正式废止了,在ARC下调用该方法会导致编译器报错。
网友评论