本文学习方向与部分内容借鉴《Objective-C高级编程iOS与OS X多线程和内存管理》一书,个人认为深入了解OC是不能错过这本书的。一个狮子脑袋的封面很容易让人印象深刻,无论是否学习过这本书的苹果开发者,大部分都听过说这本书吧,此书很经典,有兴趣的朋友可以去学习学习。
自动引用计数(ARC,Automatic Reference Counting)是指内存管理中对引用采取自动计数的技术。在Objective-C中采用ARC机制,让编译器来进行内容管理。在Apple LLVM3.0之后编译器中设置ARC为有效状态(其实现在ARC已经是默认状态了),就无需再次键入retain或release代码,这在降低程序崩溃,内存泄漏等风险的同时,很大程度上减少了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象。如此一来,应用程序将具有可预见性,其能流程运行,速度也将大幅提升。
与自动引用计数相对的那就是手动引用计数(MRC, Manual Reference Counting),在苹果 WWDC 2011 提出ARC之前,开发者就是手动管理内存的,虽然距离现在确实很遥远了,但是学习一门语言的发展中的变化,对自己的深度提高有很大的作用的哦。
书中用了开关房间灯的例子来说明引用计数的机制,例子的需求是办公室只要有人灯就应该亮着,一个人都没有灯才熄灭(不去考虑那些乱七八糟的情况)。解决这一问题的办法是使办公室在还有至少1人的情况下保持开灯状态,而在无人时保持关灯状态。1.最早进入办公室的人开灯,2.之后进入办公室的人,需要照明,3.下班离开办公室的人,不需要照明,4.最后离开办公室的人关灯(此时已无人需要照明)。这里用计数的方式来计算需要照明的人数,当办公室没有人的时候,需要照明的人数为0,每当有一个人来到办公室需要照明的人数就会加1,第一个人进入办公室需要照明的人数就变成了1,第二个人进入办公室需要照明的人数就变成了2,所以只要需要照明的人数大于1就应该开灯;每当有一个人离开办公室需要照明的人数就会减1,所以最后一个人离开的时候需要照明的人数就会变成0,因此就应该关灯。
此类例子有很多,例如开车停车,只要车上有人就应该开车,车上没人就应该停车(还是开关灯这个例子比较经典,面试的时候被问到引用计数,用这个例子来解答应该很帅)。针对开关灯这个例子,对照明设备所做的动作相当于对Objectvie-C对象所做的动作,开灯相当于生成对象,需要照明相当于持有对象,不需要照明相当于释放对象,关灯相当于废弃对象。书中运用了很多图表来描述开关灯的情况,我就不一一描述了,用我个人的想法总结就是公共的东西都可以被使用,这就衍生到了内存管理的思考方式了。
引用计算其实不应该把注意力放到计数上,更加客观,正确的思考方式是:自己生成的对象,自己所持有;非自己生成的对象,自己也可能持有;不再需要自己持有的对象释放;非自己持有的对象无法释放。这里其实可以这样理解你用公款给办公室买了一个电风扇,当然你自己可以使用电风扇,但是办公室其他的员工也是可以使用电风扇的,当你不想吹电风扇的时候就坐到一个电风扇吹不到的地方,当所有人都不用电风扇的时候,关掉电风扇,当然你是没有资格去旁边办公室关掉别人的电风扇的,并且那个电风扇和你是没有任何关系的。
生成并持有对象(alloc/new/copy/mutableCopy等方法),持有对象(retain方法),释放对象(release方法),废弃对象(dealloc方法)。这些有关objective-C内存管理的方法,实际上不包括在该语言中,而是包含在Cocoa框架中用于OS X,iOS应用开发。Cocoa框架中Foundation框架类库的NSObject类的alloc类方法,retain实例方法,release实例方法和dealloc实例方法。
alloc与new方法都是生成并持有对象,copy方法利用基于NSCopying方法约定,由各类实现的copyWithZone:方法生成并持有对象的副本。与copy方法类似,mutableCopy方法利用基于NSMutableCopying方法约定,有各类实现的mutableCopyWithZone:方法生成并持有对象的副本。两者的区别在于,copy方法生成不可变的对象,而mutableCopy方法生成可变更的对象。这类似于NSArray类对象与NSMutableArray类对象的差异。用这些方法生成的对象,虽然是对象的副本,但同alloc,new方法一样,在“自己生成并持有对象”这点上没有改变。
非自己生成的对象,自己也能持有,例如数组的array方法,字典的dictionary方法等。
id obj = 【NSMutableArray array】;
NSMutableArray类对象被赋给变量obj,但变量obj自己并不持有该对象。使用retain方法可以持有对象。
自己持有的对象,一旦不再需要,持有者有义务释放对象。释放对象使用release方法。autorelease方法,可以使取得的对象存在,但自己不持有对象。autorelease提供这样的功能,是对象在超出指定的生存范围时能够自定并正确地释放(调用release方法)。autorelease把不立即释放的对象注册到autoreleasepool中(自动释放池,就像一个池子一样,存放了所有不立即释放的对象),autoreleasepool是OC中的一种内存自动回收机制,它可以延迟加入autoreleasepool中的对象release的时机。autoreleasepool的实现原理涉及到了runloop,底层也与C++有关,有兴趣的朋友可以自行搜索查资料研究了。
对于用alloc/new/copy/mutableCopy方法生成并持有对象,或是用retain方法持有的对象,由于持有者是自己,所以在不需要该对象时需要将其释放。而由此意外所得到的对象绝对不能释放。倘若在应用程序中释放了非自己所持有的对象就会崩溃。例如自己生成并持有对象后,在释放完不再需要的对象之后再次释放。