- Effective Objective-C 2.0 读书笔记
- Effective Objective-C 2.0笔记(二)
- Effective Objective-C 2.0笔记(一)
- Effective Objective-C 2.0笔记(三)
- Effective Objective-C 2.0笔记(五)
- 《Effective Objective-C 2.0 》 阅读笔
- Effective Objective-C 2.0 无废话精简篇
- Effective Objective-C 2.0 脑图- [O
- iOS开发读书笔记:Effective Objective-C
- iOS开发读书笔记:Effective Objective-C
第五章 内存管理
第29条:理解引用计数
OC 中有手动内存管理(MRC) 自动内存管理(ARC)
手动内存管理需要程序员亲自操作对象的的释放,而ARC中就免除了繁琐的release操作。但是在iOS中不是所有对象都无需手动释放的,比如非OC(比如C语言创建的)中的对象需要手动释放,不然会存在内存泄漏的风险。
//比如使用#import <VideoToolbox/VideoToolbox.h>
CVPixelBufferRef outputPixelBuffer = NULL;
outputPixelBuffer ........
CFRelease(outputPixelBuffer);
retain count叫做“保留计数”也可以叫做“引用计数”。
NSObject 协议声明了下面三个方法用于操作计数器,以递增或递减其值:
- Reatin 递增保留计数
- release 递减保留计数
- autorelease 待稍后清理“自动释放池”(autorelease pool)时,再递减保留计数。
对象被创建出来,其保留计数至少为 1.若想令其继续存活,则调用retain方法。调用release或autorelease 直至引用计数归零时,对象就被回收了(dealllocated)。系统将会惊奇占用的内存标记为“可重用”(reuse)。此时,所有指向该对象的引用也都变得无效了。
MRC下的set方法
- (void)setFoo:(id)foo{
[foo retain];
[_foo release];
_foo = foo;
}
这个顺序很重要。加入还未保留新值就报旧值释放了,而且两个值又指向同一个对象,那么,先执行的release操作就可能导致系统将此对象永久回收。而后续的retain操作则无法令这个已经彻底回收的对象复生,于是实例变量就成了悬挂指针(野指针,僵尸对象)。
自动释放池
autorelease 会在稍后递减计数,通常是下一次“时间循环(event Loop)”时递减,不过也也可能执行的更早。此特性在方法中返回兑现时更该用它。
//MRC下的返回值方法
- (NSString *)stringValue{
NSString * str = [[NSString alloc] initWithFormat:@"I am this: %@",self];
return [str autorelease];
}
保留环 (retain cycle)
每个对象都相互引用着对方,使用“弱引用”可以打破这种状态。或是从外界命令循环中的某个对象不在保留另外一个对象。从而避免内存泄漏
要点
- 引用计数机制通过可以递增递减的计数器来管理内存。对象创建好后,其保留计数至少为1.若保留计数为正,则对象继续存活。保留计数将为0时,对象就被销毁了。
- 在对象生命中,其余对象通过引用计数来保留或释放此对象。保留与释放操作分别会递增及递减保留计数。
第30条:以ARC简化引用计数
clang编译器项目垈有一个“静态分析器”(static analyzer),用于指明程序里引用计数出问题的地方。
使用ARC时一定要记住,引用计数实际上还是要执行的,只不过保留与释放操作现在是由ARC自动为你添加。
ARC调用方法时,并不通过普通的Object-C消息派发机制,而是直接调用底层C语言版本。这样做性能更好。
使用ARC时必须遵循的方法命名规则。
将内存管理语意在方法名重表示出来早已成为Object-C的惯例,而ARC则将之确立为硬性硬性规定。若方法名以下列词语开头,则其返回的对象归调用者所有:
- alloc
- new
- copy
- mutableCopy
在编译器ARC会把能够相互抵消的retain、release、autorelease操作简约。如果发现在同一个对象上执行多次“保留”与“释放”操作,那么ARC有时可以成对的移除这两个操作。
其ARC可以直接舍弃autorelaese这个概念,并且规定,所有从方法中返回的对象保留计数都比期望值多1,但是,这样做就破环了想后兼容性。
不过,ARC可以运行期检测到这一对多余操作,也就是autorelease及紧跟其后的retain。为了优化代码,在方法中返回自动释放的对象时,要执行一个特殊函数,测试不直接调用对象的autorelease方法,而是改为调用objc_autoreleaseRetrunValue。此函数会检视当前方法返回之后即将执行的那段代码。若发现那段代码要在返回的对象上执行retain操作,则设置全局数据结构(测数据结构机体内容因处理器而异)中的一个标志位,而不执行autorelease操作。与之相识,如果方法返回一个自动释放的对象,而调用方法的代码要保留此对象,那么测试不执行retain,而是改为执行object_retainAutoreleaseRetainValue函数。此函数要检测刚才那个标志位,若已经置位,这不执行retain操作。设置并检测标志位,要比调用autorelease和retain更快。
CoreFoundation下不由ARC管理。需要手动释放。比如
- (void)dealloc{
CFRelease(_coreFoundationObject);
free(_heapAllocatedMemoryBlob);
}
要点
- 有“ARC”之后,程序员就无需担心内存管理问题了,使用ARC来编程,可省去类中的许多“样板代码”。
- ARC管理对象生命周期的办法基本上就是:在适合的地方插入“释放”及“保留”操作。在ARC环境下,变量的内存干里语意可以通过修饰符指明,而原来则需要手动执行“保留”及“释放”操作。
- 由方法所返回的对象,其内存管理语义总是通过方法名来提现。ARC将此确定为开罚者必须遵守的规定。
- ARC只负责管理Object-C对象内存,尤其要注意:CoreFoundation对象不归ARC管理,开发者必须适时调用CFRetain/CFRelease。
第31条:在dealloc方法中只释放引用并解除监听
对象在经历其生命周期后,最终会为系统所回收,这时就要执行dealloc方法了,在每个对象的生命周期内,此方法仅执行一次,也就是当保留计数将为0的时候,具体何时执行无法确定。
要点
- 在dealloc方法里,应该做的事情就是释放就是释放指向其他对象的引用,并取消原来订阅的“键值观测”(KVO)或NSNotificationCenter等通知,不要做其他事情。
- 如果对象持有文件描述符号等系统资源,那么应该专门编写一个方法来释放此种资源。这样的类要和其他使用者约定:用完资源后必须调用close方法。
- 执行异步任务的方法不应该在dealloc方法中调用;只能在正常状态下执行的那些方法也不应该在dealloc里调用,因为此时对象已处于正在回收的状态了。
第32条:编写“异常安全代码”时留意内存管理问题
要点
- 捕获异常时,一定要注意将try块内的所创建的对象清理干净。
- 在默认情况下,ARC不会生成安全处理异常所需的清理代码。开启编译器标示后,可生成这种代码,不过会导致应用程序变大,而且会降低运行效率。
第33条:以弱引用避免保留环
最简单的保留环由两个对象组成。如果没有别的对象可以引用环中的对象导致这个环无法被访问,他们就会继续存活下去,造成内存泄漏。
要点
- 将某些引用设置为weak,可避免出现“保留环”。
- weak引用可以自动清空,也可以不自动清空。自动清空(autonilling)是随着ARC而引入的新特性,由运行期系统来实现。在具备自动清空功能的弱引用上,可以随意读去其数据,因为这种引用不会指向已经回收过的对象。
第34条:以“自动释放池块”降低内存峰值
整理要理清自动释放池的原理和自动释放池与runloop的关系
线程、自动释放池、RunLoop的爱恨情仇
探究自动释放池的实现
要点
- 自动释放池排布在栈中,对象收到autorelease消息后,系统将其放入最顶端的池里。
- 合理使用自动释放池,可降低应用程序的内存峰值。
- @autoreleasepool这种新型式写法能创建出更为轻量级的制动释放池。
第35条:用“僵尸对象”调试内存管理问题
要点
- 系统在回收对象时,可以不将其真的回收,而是把它转化为僵尸对象,通过环境变量NSZombieEnable可开启此功能。
- 系统会修改该对象的isa指针,令其指向特殊的僵尸类,从而使该对象变为僵尸对象。僵尸类能够响应所有选择子,响应方式为:打印一条包含消息内容及其接收者的消息,然后终止应用程序。
第36条:不要使用retainCount
要点
- 对象的保留计数看似有用,实则不然,因为任何给定时间点上的“绝对保留计数”(absolute retain count)都无法反映对象生命期的全貌。
- 应用ARC之后,retainCount方法就正式废止了,在ARC下调用发方法就会导致编译器报错。
网友评论