Objective-C中提供了两种内存管理机制MRC(MannulReference Counting)和ARC(Automatic Reference Counting),分别提供对内存的手动和自动管理,来满足不同的需求.
ARC:
ARC是Auto Reference Counting的缩写,即自动引用计数,由编译器在代码合适的位置中自动添加retain/Release/Autorelease/dealloc方法从而进行内存管理.
ARC几个要点:
- 在对象被创建时 retain count +1,在对象被release时 retain count -1.当retain count 为0 时,销毁对象。
- 程序中加入autoreleasepool的对象会由系统自动加上autorelease方法,如果该对象引用计数为0,则销毁。
那么ARC是为了解决什么问题诞生的呢?这个得追溯到MRC手动内存管理时代说起。
MRC下内存管理的缺点:
- 当我们要释放一个堆内存时,首先要确定指向这个堆空间的指针都被release了。(避免提前释放)
- 释放指针指向的堆空间,首先要确定哪些指针指向同一个堆,这些指针只能释放一次。(MRC下即谁创建,谁释放,避免重复释放)
- 模块化操作时,对象可能被多个模块创建和使用,不能确定最后由谁去释放。
- 多线程操作时,不确定哪个线程最后使用完毕
在ARC中与内存管理有关的标识符,可以分为变量标识符(_strong, _weak, _unsafe_unretained, autoreleasing)和属性标识符(nonatomic/atomic, assign/retain/strong/weak/unsafe_unretained/copy,readonly/readwrite),对于变量默认为__strong,而对于属性默认为unsafe_unretained。也存在autoreleasepool。
其中assign/retain/copy与MRC下property的标识符意义相同,strong类似与retain,assign类似于unsafe_unretained,strong/weak/unsafe_unretained与ARC下变量标识符意义相同,只是一个用于属性的标识,一个用于变量的标识(带两个下划短线__)。所列出的其他的标识符与MRC下意义相同。
(1)对于assign,你可以对标量类型(如int)使用这个属性。你可以想象一个float,它不是一个对象,所以它不能retain、copy。
(2)对于copy,指定应该使用对象的副本(深度复制),前一个值发送一条release消息。基本上像retain,但是没有增加引用计数,是分配一块新的内存来放置它。特别适用于NSString,如果你不想改变现有的,就用这个,因为NSMutableString,也是NSString。
MRC:
在MRC的内存管理模式下,与对变量的管理相关的方法有:retain,release和autorelease。retain和release方法操作的是引用记数,当引用记数为零时,便自动释放内存。并且可以用NSAutoreleasePool对象,对加入自动释放池(autorelease调用)的变量进行管理,当drain时回收内存。
Strong 和 Weak 的区别:
强引用持有对象,弱引用不持有对象。
强引用可以释放对象,但弱引用不可以,因为弱引用不持有对象,当弱引用指向一个强引用所持有的对象时,当强引用将对象释放掉后,弱引用会自动的被赋值为nil,即弱引用会自动的指向nil。
Strong 强引用,举个例子:
1 id __strong test0 = [[NSObject alloc] init]; /* 设为对象A*/ 2 3 id __strong test1 = [[NSObject alloc] init];/*设为对象B*/
test0 和 test1 都是强引用,test0是对象A的持有者,就是拥有A,test1是对象B的持有者,就是拥有对象B,若:
test1 = test0;/*对象A的持有者就变成了test1*/
这样对象B就没有了持有者,没有持有者的对象会被ARC回收,就是释放,这样:
test1持有对象A,test0也持有对象A。
weak 弱引用,主要作用是用来防治循环引用出现内存泄漏的问题,它主要是弱引用,弱引用就是不持有对象,只是指向这个对象,举个例子:
1 id __strong test0 = [[NSObject alloc] init]; /* 设为对象A*/ 2 3 id __strong test1 = [[NSObject alloc] init];/*设为对象B*/ 4 5 id __weak test2 = test0;/*test1持有对象A的弱引用*/
test0持有对象A的强引用,而test2持有对象A的弱引用,也就是说,test0还是持有A的,而test2弱引用了test0的对象A,并没有持有对象A,当test2离开了作用域,对对象A的引用就会失去,当对象A被释放掉之后,test2会被置为nil,并不会出现crash。若:
test1 = test0;/*test1强引用对象A*/
此时对象B因为没有持有者就会被释放。
再如:
1 #import <Foundation/Foundation.h> 2 3 int main(int argc, const char * argv[]) { 4 @autoreleasepool { 5 id __weak obj0 = nil; 6 if (YES) { 7 id obj1 = [[NSObject alloc] init]; //默认为强引用,即为strong类型 8 obj0 = obj1; 9 NSLog(@"obj0: %@", obj0); 10 } 11 NSLog(@"obj0: %@", obj0); 12 } 13 return 0; 14 } 15 16 /* 17 * 输出结果 18 * obj0: <NSObject: 0x1003066c0> 19 * obj0: (null) 20 * 21 * 因为obj1生成的默认的为强引用(__strong),在超出if的作用域之后,obj1所持有的对象被释放, 22 * obj0为弱引用,所以obj0不持有对象,在obj1对象释放后,obj0自动的被赋值为nil 23 * 弱引用的特性是,不持有对象,即便是写成id __weak obj1 = [[NSObject alloc] init]; 24 * 此代码系统会给与警告,因为这里obj1被声明成弱引用,那么在赋值之后,alloc出来的对象会被立即释放。 25 */