ARC所做的事情并不仅仅局限于在编译期找到合适的位置帮你插入合适的release这样的内存管理方法;ARC在运行期也做了一些优化: 比如在某些情况下妙地跳过 autorelease 机制; 比如如果发现在同一个对象上执行了多次"retain"与"release"操作,那么ARC有时可以成对的抵消这两个操作。
objc_autoreleaseReturnValue这个函数通常会返回注册到自动释放池的对象。它的作用是检视稍后执行的代码是否会执行retain方法,如果有则会把某个专用于检测的变量或者说数据结构的标志位置位,并直接返回对象,不再去执行autorelease方法;
objc_retainAutoreleasedReturnValue这个函数则是访问专用于检测的变量的标志位,看其是否
有置位,如果有则直接返回对象;否则则对对象执行retain方法。
在ARC中使用非alloc/new/copy/mutableCopy等开头生成对象的方法,就会触发objc_autoreleaseReturnValue 和 objc_retainAutoreleaseReturnValue 这两个方法;
源码如下:
咱们用以下的代码来讲解:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
id array = [NSMutableArray arrayWithCapacity:1];
id __unsafe_unretained array_1 = [NSMutableArray array];
id array_2 = [NSMutableArray array];
id __weak weakArray = [NSMutableArray arrayWithCapacity:1];
id __unsafe_unretained unsaferetainedArray = [NSMutableArray arrayWithCapacity:1];
NSLog(@"array: %p", array);
NSLog(@"array_1: %p", array_1);
NSLog(@"array_2: %p", array_2);
NSLog(@"weakArray: %p", weakArray);
NSLog(@"unsaferetainedArray: %p", unsaferetainedArray);
extern void _objc_autoreleasePoolPrint(void); // 打印注册到autoreleasePool中的对象
_objc_autoreleasePoolPrint();
});
AveryProject[26439:3141177] array: 0x600003e32850
AveryProject[26439:3141177] array_1: 0x600003e32b80
AveryProject[26439:3141177] array_2: 0x600003e32be0
AveryProject[26439:3141177] weakArray: 0x0
AveryProject[26439:3141177] unsaferetainedArray: 0x600003e32c10
objc[26439]: ##############
objc[26439]: AUTORELEASE POOLS for thread 0x7000001f2000
objc[26439]: 3 releases pending.
objc[26439]: [0x7fb9f3004000] ................ PAGE (hot) (cold)
objc[26439]: [0x7fb9f3004038] ################ POOL 0x7fb9f3004038
objc[26439]: [0x7fb9f3004040] 0x600003e32b80 __NSArrayM
objc[26439]: [0x7fb9f3004048] 0x600003e32c10 __NSArrayM
objc[26439]: ##############
如上所示用__unsafe_unretained来修饰的array_1 & unsaferetainedArray被加入到 autoreleasepool中了;
arrayWithCapacity: & array这两个NSMutableArray生成array的方法不是以alloc/new/copy/mutableCopy开头的、所以方法内部会触发objc_autoreleaseReturnValue,
由于array_1 & unsaferetainedArray都是用__unsafe_unretained来修饰的、所以生成的对象会被加入到autoreleasepool中去;
array & array_2是两个强引用的指针、生成的对象不会加入到autoreleasepool而是直接返回该对象;
weakArray没有被加入到autoreleasepool的原因是生成后即被释放了;
再来讲解一下 "__unsafe_unretained & __weak & __autoreleasing 都有什么区别" 中的案例:
(下面的这部分代码并不会引起Crash~)
id __unsafe_unretained uu_obj = nil;
{
id array = [[self class] getArray];
NSLog(@"retainCount: %lu", CFGetRetainCount((__bridge CFTypeRef)(array))); // 2
[array addObject:@"Avery"];
uu_obj = array;
NSLog(@"retainCount: %lu", CFGetRetainCount((__bridge CFTypeRef)(array))); // 2
}
NSLog(@"uu_obj = %@", uu_obj);
+ (id)getArray {
return [NSMutableArray array];
}
getArray方法的汇编如下:
/**
id obj = objc_msgSend(NSMutableArray, @selector(array));
return obj;
*/
getArray方法内部通过"[NSMutableArray array]"生成的对象会被加入到autoreleasepool中、"id array = [[self class] getArray];"强指针array指向生成的对象会使该对象的引用计数+1;
再来看看另外一个会导致Crash的案例:
id __unsafe_unretained uu_obj = nil;
{
id array = [[self class] copyObject];
NSLog(@"retainCount: %lu", CFGetRetainCount((__bridge CFTypeRef)(array))); // 1
[array addObject:@"Avery"];
uu_obj = array;
NSLog(@"retainCount: %lu", CFGetRetainCount((__bridge CFTypeRef)(array))); // 1
}
NSLog(@"uu_obj = %@", uu_obj);
+ (id)copyObject {
return [NSMutableArray array];
}
copyArray方法的汇编如下:
/**
id obj = objc_msgSend(NSMutableArray, @selector(array));
return _objc_retainAutoreleasedReturnValue(obj);
*/
copyArray方法内部通过"[NSMutableArray array]"生成的对象本应也会被加入到autoreleasepool中、但是通过copy开头的方法如果生成的对象被加入到了autoreleasepool的话、编译器会认为有一定的隐患所以编译器就在copyArray方法的结尾添加objc_retainAutoreleasedReturnValue方法、objc_retainAutoreleasedReturnValue方法的使用会使直接返回array对象、避免生成的对象被加入到autoreleasepool中去;
【 请勿直接转载 - 节约能源从你我做起 】