objc_autoreleaseReturnValue & objc_retainAutoreleasedReturnValue

ARC所做的事情并不仅仅局限于在编译期找到合适的位置帮你插入合适的release这样的内存管理方法;ARC在运行期也做了一些优化: 比如在某些情况下妙地跳过 autorelease 机制; 比如如果发现在同一个对象上执行了多次"retain"与"release"操作,那么ARC有时可以成对的抵消这两个操作。

objc_autoreleaseReturnValue这个函数通常会返回注册到自动释放池的对象。它的作用是检视稍后执行的代码是否会执行retain方法,如果有则会把某个专用于检测的变量或者说数据结构的标志位置位,并直接返回对象,不再去执行autorelease方法;
objc_retainAutoreleasedReturnValue这个函数则是访问专用于检测的变量的标志位,看其是否
有置位,如果有则直接返回对象;否则则对对象执行retain方法。

在ARC中使用非alloc/new/copy/mutableCopy等开头生成对象的方法,就会触发objc_autoreleaseReturnValue 和 objc_retainAutoreleaseReturnValue 这两个方法;

源码如下:


objc_autoreleaseReturnValue

objc_retainAutoreleasedReturnValue



咱们用以下的代码来讲解:

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中去;



【 请勿直接转载 - 节约能源从你我做起 】

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354