目录
1. 引用计数
在ARC时代,为什么我们还要理解MRC下的引用计数?原因主要有二:
-
ARC并非万能。理解引用计数是我们解决循环引用的基础;
-
了解引用计数可以加深对OC内存管理的理解。
1.1 MRC vs ARC
新开1个工程,在Build Phases的AppDelegate.m里添加-fno-objc-arc,可以开启MRC模式。
_ARC/MRC command.png)
输入如下代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// AppDelegate.m
#define isARC 1
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
#if isARC == 0 //-fno-objc-arc
NSObject *object = [[NSObject alloc] init];
NSLog(@"Reference Count = %u", [object retainCount]); //1
NSObject *anotherObject = [object retain];
NSLog(@"Reference Count = %u", [anotherObject retainCount]); //2
[anotherObject release];
NSLog(@"Reference Count = %u", [anotherObject retainCount]); //1
[object release];
NSLog(@"Reference Count = %u", [anotherObject retainCount]); //1, why not 0?
#elif isARC == 1
NSObject *object = [[NSObject alloc] init];
NSLog(@"Reference count = %ld", CFGetRetainCount((__bridge CFTypeRef)object)); //1
//[object retain];
NSObject *anotherObject = object;
NSLog(@"Reference count = %ld", CFGetRetainCount((__bridge CFTypeRef)object)); //2
anotherObject = nil;
//[object release];
NSLog(@"Reference count = %ld", CFGetRetainCount((__bridge CFTypeRef)object)); //1
object = nil;
//[object release];
NSLog(@"Reference count = %ld", CFGetRetainCount((__bridge CFTypeRef)object)); //Thread 1: EXC_BREAKPOINT(code=EXC_I386_BPT,subcode=0X0)
#endif
return YES;
}
在MRC模式下(-fno-objc-arc),我们可以看到:
1.在实例化NSObject时,object指向了该实例,因此该实例retainCount从0变为1。
2.anotherObject也指向该实例,并且retain,则retainCount从1变为2。如果是用=object而不是=[object retain]则retainCount依旧是1。
3.anotherObject release使得该实例retainCount-1。
4.object release使得该实例retainCount-1变为0释放。但是为什么retainCount输出还是1呢?因为该对象的内存已经被回收,而我们向一个已经被回收的无效对象发了一个retainCount消息,所以它的输出结果应该是不确定的。不将无效对象的这个值从1变成0,可以减少一次内存的写操作,加速对象的回收。
在ARC模式下(删除-fno-objc-arc),retain和release是由系统自动加入到适当的位置的:
-
实例化过程引用计数和MRC一样。若增加
__weak修饰符,则NSObject实例之初就会被释放。 -
这和3一样。
-
object = nil会使实例retainCount减1。这里减1后为0。 -
向已释放的实例发送
CFGetRetainCount((__bridge CFTypeRef)object)(ARC下retainCount已被禁止)会触发野指针错误。
2 ARC4种所有权修饰符
属性修饰符对应的4种所有权修饰符关系如下。
2.1 __weak
表示弱引用,对应属性修饰符weak。弱应用对对象的生命周期无影响。对象在被释放的同时,指向它的弱引用会被自动置为nil,这个技术称为zeoring weak pointer。这样有效地防止了野指针的产生。__weak用在防止循环引用的场景中(如delegate,block的weak strong dance和无效的强引用(如@IBOutlet,viewController->view->subView,没有必要再viewController->subview)。
2.2 __strong
表示强引用,对应属性修饰符strong和copy。当所有对象没有任何一个强引用时,才会被释放。如果在声明时不加修饰符,默认是强应用。当需要释放强引用指向的对象时,需要将强引用置nil。
2.3 __autoreleasing
__autoreleasing在ARC中主要用在参数传递返回值(out-parameters)和引用传递参数(pass-by-reference)的情况下。具体见Autoreleasing。
2.4 __unsafe_unretained
和__weak一样,但对象释放后,不会自空,所以非常不安全。由于ARC是在iOS 5引入的,而这个修饰符主要是兼容iOS 4以及更低版本的设备,现在可以完全忽略这个修饰符了,因为iOS 4早已退出历史舞台很多年。
2 ARC下注意事项
2.1 Retain Cycle 问题及解决
利用instrument leak
1
2
3
4
NSMutableArray *array1 = [NSMutableArray new];
NSMutableArray *array2 = [NSMutableArray new];
[array1 addObject:array1];
[array2 addObject:array1];
当有如上引用循环时,可以利用instrument的leak来查看,具体步骤如下:
-
Xcode -> Product -> Profile -> Leak;
-
RecordingCycles & Roots会清楚显示循环引用的对象。
3 总结
本文总结成下图。
_ARC/OC内存管理总结.png)