目录
- 1. 引用计数
- 2 ARC4种所有权修饰符: __weak,__strong,__autoreleasing,__unsafe_unretained
- 2. Retain Cycle 问题及解决
- 3 总结
1. 引用计数
在ARC时代,为什么我们还要理解MRC下的引用计数?原因主要有二:
-
ARC并非万能。理解引用计数是我们解决循环引用的基础;
-
了解引用计数可以加深对OC内存管理的理解。
1.1 MRC vs ARC
新开1个工程,在Build Phases的AppDelegate.m
里添加-fno-objc-arc
,可以开启MRC模式。
输入如下代码。
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种所有权修饰符: __weak,__strong,__autoreleasing,__unsafe_unretained
属性修饰符对应的4种所有权修饰符如下所示:
2.1 __weak
表示弱引用,对应属性修饰符weak
。弱应用对对象的生命周期无影响。对象在被释放的同时,指向它的弱引用会被自动置为nil,这个技术称为zeoring weak pointer。这样有效地防止了野指针的产生。__weak
用在防止循环引用的场景中(如delegate,block)和无效的强引用中(如@IBOutlet)。
2.2 __strong
表示强引用,对应属性修饰符strong
和copy
。当所有对象没有任何一个强引用时,才会被释放。如果在声明时不加修饰符,默认是强应用。当需要释放强引用指向的对象时,需要将强引用置nil。
2.3 __autoreleasing
2.4 __unsafe_unretained
和__weak一样,但对象释放后,不会自空,所以非常不安全。由于ARC是在iOS 5引入的,而这个修饰符主要是兼容iOS 4以及更低版本的设备,现在可以完全忽略这个修饰符了,因为iOS 4早已退出历史舞台很多年。
2. Retain Cycle 问题及解决
。
2.1 Retain Cycle 问题
3 总结
。