1 传统内存管理 ide
Objective-C对象的生命周期能够分为:建立、存在、消亡。 函数
1.1 引用计数 ui
相似Java,Objective-C采用引用计算(reference counting)技术来管理对象的生命周期。每一个对象都定义有一个整数(称引用计数器)与之相关联,该数用以表示当前有多少个指针指向该对象。 spa
1.1.1 操做方法 3d
当某段代码须要访问一个对象时,该代码就将对象的保留计数值加1;当结束访问时就减1;若引用计数器减到0时,该对象将被销毁。引用计数器的值由以下三种操做进行控制: 指针
1) 建立 code
当使用alloc、new方法或者经过copy消息(接收到消息的对象会建立一个自身的副本)建立一个对象时,对象的保留计数器值就被初始化为1。 对象
2) 增长 blog
要增长对象的引用计数器值,能够给对象发送一条retain消息,即调用对象的retain方法。 教程
3) 减小
要减小对象的引用计数器值,能够给对象发送一条release消息,即调用对象的release方法。
当一个对象因其引用计数器值为0时,将被系统销毁,从而系统自动给该对象发送一条dealloc消息。因此用户能够重载对象的dealloc方法,dealloc方法至关是C++的虚构函数,能够在该函数中释放申请的内存空间。
表 11 NSObject类内存管理方法
方法 |
描述 |
- (instancetype)retain |
将引用计数器的值加1,可由用户调用。 |
- (oneway void)release |
将引用计数器的值减1,可由用户调用。 |
- (NSUInteger)retainCount |
获取引用计数器的值,可由用户调用。 |
- (instancetype)autorelease |
将对象添加到自动释放池中,可由用户调用。 |
- (struct _NSZone *)zone |
复制方法。 |
以下所示是RetainTracker对象生命周期的引用计数器值:
1.1.2 对象全部权
对象全部权是指实体的一种职责,当某个实体"拥有一个对象"时,就意味着该实体要负责对其拥有的对象进行清理。实体可能拥有对象的状况有:
若是一个对象内由指向其余对象的实例变量,则称该对象拥有这些对象;
若是一个函数建立了一个对象,则称该函数拥有这个对象。
1.1.3 访问方法
将类中的成员指针设置为指向一个外部对象,需经过retain和release方法来操做引用计数值,以下有3种操做方式:
1) 简单赋值
简单赋值方式,以下所示:
2) 修复赋值
这种方式是对前一种方式的修复,即修复了未对原来成员变量的引用计数值进行减小操做,但仍存在问题,以下所示:
3) 正确赋值
这种方式是正确的赋值方式,修复了前两种错误方式。即修复了未对原来成员指针的引用计数值操做,也修复了可能出现同一个指针的问题,以下所示:
1.2 自动释放池
内存管理是一个棘手的问题,如上述所示的setter方法的各类细微问题。因此Cocoa引入了自动释放池(autorelease pool)概念,这种方式是经过自动释放池来管理引用计数值的release操做。有两种方式建立自动释放池:
1.2.1 使用方式
若要使用自动释放池来管理对象的release操做,只要在自动释放池的生命周期内调用被管理对象的autorelease方法,便可将对象的引用计数值委托自动释放池实体来管理,当自动释放池实体结束时,将会调用池中对象的release方法,而且只调用一次。以下所示:
1) 关键字方式
@autoreleasepool方式的生命周期是从左花括号"{"开始,直到右花括号"}"结束,即执行到了右花括号时,那么将调用自动释放池中对象的release方法,来减小相应的引用计数值。
2) 对象方式
NSAutoreleasePool对象的生命周期是从调用其new方法来建立对象开始,直到调用池对象的release方法后,由系统调用释放池对象的dealloc方法后结束,即自动释放池在dealloc"虚构函数"中调用被管理对象的release方法来减小相应引用计数值。
1.2.2 释放池结构
自动释放池以栈的形式实现:当建立了一个新的自动释放池时,该池就被添加到栈顶中。因此若某个对象调用autorelease方法时,该对象将被放入最顶端(栈顶)的自动释放池中,而且栈顶下的释放池仍未被销毁,即被添加到栈顶下面释放池的对象仍未被释放。
若须要由自动释放池来管理大量对象时,用户能够手动销毁释放池,而后再建立新的池对象,以下所示:
Cocoa也使用相似的方法来管理内存,当使用AppKit时,Cocoa会按期自动地为用户建立和销毁自动释放池。一般是在程序处理当前事件(如鼠标单击或鼠标按下)之后执行这些操做。
1.3 Cocoa内存管理规则
Cocoa有许多内存管理约定,它们简化了retain、release和autorelease的使用方法,这些规则有:
2 自动引用计数
自动引用计数为Automatic Reference Counting (ARC) ,是Objective-C提供了一种自动内存管理的功能。ARC不须要用户考虑retain和release操做,而是在编译期添加代码(retain和release等方法)来保障对象的生命周期,同时可为对象自动生成合适的dealloc方法。从而让用户专一那些感兴趣的代码。以下是引用计数器手动和自动的差别:
图 21 引用计数器的手动和自动实现的差别
自动的引用计数和手动引用计数方法是互斥的,即不能同时在应用程序中使用ARC技术和手动操做retain和release。如在图 22所示,若选择YES时(默认),则启动ARC功能;若NO,则关闭ARC功能。
图 22 ARC功能启动设置
2.1 强制规则
为了ARC能工做,强制规定了一些新规则,而且这些规则不能在其它编译器使用。若是用户违反了这些规则,那么将获得一个compile-time错误。这些规则为:
1) 不能显示调用dealloc方法,同时不能调用和重载retain、release、retainCount和autorelease方法。但能够重载dealloc方法,固然在dealloc方法中不能调用引用计数器管理方法,同时在dealloc方法中不能调用 [super dealloc],父类的dealloc方法由编译器自动添加。
2) 不可以使用NSAllocateObject和NSDeallocateObject,但仍可以使用alloc方法建立对象。
3) 不可以使用NSAutoreleasePool对象,但可使用@autoreleasepool代码块。
4) 属性名次不能以new开头,好比说@property NSString *newString是不容许的。
5) 属性不能只有一个read-only而没有其它内存管理特性。若没有启用ARC功能,可使用@property (readonly) NSString *title语句,但若是启用来ARC功能,就必须指定由谁来管理内存。
6) 结构体(struct)和联合体(union)不能使用ROP做为成员,如struct {NSString *str}代码是不被容许.
ARC只对可保留的对象指针(ROPs)有效。可保留的对象指针主要有以下三种:
全部其它指针类型,好比char*和CF对象都不支持ARC特性,若是使用的指针不支持ARC,那么必须手动管理这些对象空间。 |
2.2 变量修饰词
2.2.1 保留环
使用引用计数机制时,常常要注意的一个问题是"保留环"(retain cycle),即呈环状相互引用的多个对象。这将致使内存泄漏,由于循环中的对象其保留计数不会降为0。对于循环中的每一个对象来讲,至少还有另外一个对象引用着它。
如图 23所示,A的引用计数为2,而B的引用计数为1。当A的拥有者"云"release A后,A和B的引用计数都为1,致使二者都没法被释放。
图 23 引用计数保留环
2.2.2 修饰词
目前Objective-C提供4种修饰词(qualifier)来修饰变量,具体语义为:
表 21 Objective-C修饰词
类型 |
语义 |
__strong |
默认修饰词,当有一个strong指针指向某对象,那么该对象将一直保持"活跃"状态; |
__weak |
其不能让被引用对象一直保持"活跃"状态。当某个被引对象没有__strong指针指向它时,那么其它对象以__weak类型指向上述对象的指针将被自动置为nil。 |
__unsafe_unretained |
声明一个弱应用,可是不会自动nil化,也就是说,若是所指向的内存区域被释放了,这个指针就是一个野指针了。 |
__autoreleasing |
用来修饰一个函数的参数,这个参数会在函数返回的时候被自动释放。 |
其使用形式为:
对于图 23所示的引用环能够采用弱引用解决,由于在指向的对象释放以后,这些弱引用就会被设置为nil。如图 24所示,带有__weak的引用环结构,当"云"向A发送release消息后,A的引用计数为0,从而释放A和B对象内存空间。
图 24 带有弱引用的环
2.3 Toll-Free Bridging管理
ARC仅支持可保留对象指针,而没法自动管理Core Foundation对象的空间,必须由用户手动调用CFRetain 和 CFRelease方法来管理Core Foundation对象。若是在Objective-C 和 Core Foundation-style 对象之间进行转换时,为了让ARC便于工做,那么须要告诉编译器哪一个对象是指针的拥有者。为此Objective-C使用了桥接转换(bridged cast)技术。
1) __bridge类型操做符
这种操做符是从ROP类型转换为non-ROP类型,但只传递指针并不会传递它的全部权,即指针的全部权仍在转换以前的对象上。以下所示:
2) __bridge_retained类型操做符
这种操做符将指针全部权从ROP上转移到non-ROP上。由于ARC只会注意到non-ROP,因此用户须要经过non-ROP手动释放其保留计数器的值。这个转换类型会给non-ROP对象的保留计数器加1,因此须要手动让它减1,这与标准的内存管理方式相同。以下所示:
3) __bridge_transfer类型操做符
这种转换符与上一个相反,它将全部权从non-ROP上转移到ROP上,从而ARC拥有对象并能确保它会像其它ARC对象同样获得释放。
3 参考文献
[1] Advanced Memory Management programming guide
[2] Transitioning to ARC Release Notes
[3] Objective-C基础教程(第2版)
[4] Effective Objective-C 2.0