AutoreleasePool和__weak有什么关联?

如果访问用 __weak 修饰符的变量,那么其引用的对象会被加入到autoreleasepool ?
如果大量地使用__weak修饰符的变量,注册到autoreleasepool的对象也会大量增加 ?


前言

有关objc_autoreleaseReturnValue & objc_retainAutoreleasedReturnValue的知识点若还不清楚、还请先自行查找;
先来看下如下的代码:

{
    NSMutableArray *array = [NSMutableArray array];
    array = [NSMutableArray array];
    array = [NSMutableArray array];
    array = [NSMutableArray array];
        
    /**
     以上这4个array是4个不同的对象;
     上面生成的array不会被加入到autoreleasePool中去;
    */
    extern void _objc_autoreleasePoolPrint(void);  // 打印注册到autoreleasePool中的对象 
   _objc_autoreleasePoolPrint();
}
{
    UIImage *image = [UIImage imageNamed:@"emoji_default"];
    image = [UIImage imageNamed:@"emoji_default"];
    image = [UIImage imageNamed:@"emoji_default"];
    image = [UIImage imageNamed:@"emoji_default"];
        
     /**
     这4个image均执向同一个对象 (和image的缓存有关);
     上面生成的image会被加入到autoreleasePool中去;
     添加到autoreleasePool中的image的个数为: (N-1);  // N表示image的个数
     被注册到autoreleasePool中的对象除了image还会包含UITraitCollection、CUIMutableStructuredThemeStore等其他对象;
    */
    extern void _objc_autoreleasePoolPrint(void);  // 打印注册到autoreleasePool中的对象
    _objc_autoreleasePoolPrint();
}

所以如果在代码中的某一块需要频繁的使用 [UIImage imageNamed:]方法时、最好要用@autoreleasePool{}包裹一下、要不然可能会导致内存占用峰值的激增。


子线程与AutoreleasePool有什么关系?

在新建的子线程中是被autoreleasePool包含的,当线程退出的时候会清空autoreleasePool。
参考:stackoverflow

push

pop

虽然说在子临时线程中会进行autorelease变量的回收、但是在创建子线程的时候、你的代码最好还是写在@autoreleasePool{}中,总不至于等到线程exit的时候再回收垃圾吧,你品一下常驻线程~ 如果等到线程退出再回收垃圾那岂不是早就死翘翘了。
再补充一句:创建常驻子线程的时候、最好还是用NSRunloop、少用CFRunloop的api。
每一个线程都会维护自己的 autoreleasepool 堆栈,换句话说 autoreleasepool 是与线程紧密相关的,每一个 autoreleasepool 只对应一个线程。


什么情况下访问用 __weak 修饰符的变量,那么其引用的对象会被加入到autoreleasepool ?

- (void)call_in_main {   // 主线程中调用
    TestObject *obj = [[TestObject alloc] init];
    __weak id weakObj = obj;   // obj | weakObj不会被添加到autoreleasePool中去
    [weakObj noAction];
    NSLog(@"weakObj: %@",weakObj);

    extern void _objc_autoreleasePoolPrint(void);  // 打印注册到autoreleasePool中的对象
    _objc_autoreleasePoolPrint();
}



下面来针对TestObject中的3个实例方法进行测试、本文将会在子线程中进行测试(主线程中太多对象被注册到了autoreleasePool中去、不利于调试与分析):

- (NSMutableString *)testMethod_2 {
    NSMutableString *name = [[NSMutableString alloc] initWithString:@"Avery"];
    NSLog(@"testMethod_2 - name: %p", name);
    return name;
}

- (NSMutableArray *)testMethod_3 {
    NSMutableArray *array = [NSMutableArray array];
    [array addObject:@"Avery"];
    NSLog(@"testMethod_3 - array: %p", array);
    return array;
}

- (void)testMethod_4 {
    for (NSUInteger i = 0; i < 3; i++) {
        NSMutableArray *array = [NSMutableArray arrayWithCapacity:3];
        [array addObject:[NSString stringWithFormat:@"Avery - %ld",i]];
        NSLog(@"testMethod_4 - array: %p", array);
    }
}
- (void)call_in_subThread {
    TestObject *obj = [[TestObject alloc] init];
    // [obj testMethod_2];  // 用obj调用时不会使testMethod_2方法中生成的 MutableString 被添加到autoreleasePool中
    __weak id weakObj = obj;
    [weakObj testMethod_2];  // 用weakObj调用时会使testMethod_2方法中生成的 MutableString 被添加到autoreleasePool中去
    
    extern void _objc_autoreleasePoolPrint(void);  // 打印注册到autoreleasePool中的对象
    _objc_autoreleasePoolPrint();
}
- (void)call_in_subThread {
    TestObject *obj = [[TestObject alloc] init];
    __weak id weakObj = obj;
     [weakObj testMethod_4];   // 用weakObj调用时不会使testMethod_4方法中生成的 NSMutableArray 被添加到autoreleasePool中
    
    extern void _objc_autoreleasePoolPrint(void);  // 打印注册到autoreleasePool中的对象
    _objc_autoreleasePoolPrint();
}
- (void)call_in_subThread {
    TestObject *obj = [[TestObject alloc] init];
    // [obj testMethod_3];  // 用obj调用时不会使testMethod_3方法中生成的 NSMutableArray 被添加到autoreleasePool中
    __weak id weakObj = obj;
     [weakObj testMethod_3];   // 用weakObj调用时会使testMethod_3方法中生成的 NSMutableArray 被添加到autoreleasePool中去
    
    extern void _objc_autoreleasePoolPrint(void);  // 打印注册到autoreleasePool中的对象
    _objc_autoreleasePoolPrint();
}
- (void)call_in_subThread {
    TestObject *obj = [[TestObject alloc] init];
    __weak id weakObj = obj;
    [obj testMethod_2];
    [obj testMethod_3];
    [obj testMethod_4];
    
    extern void _objc_autoreleasePoolPrint(void);  // 打印注册到autoreleasePool中的对象
    _objc_autoreleasePoolPrint();
}
AveryTest[56451:20807849] currentThread(autoreleasePool_Action): <NSThread: 0x600001439d80>{number = 1, name = main}
AveryTest[56451:20807927] testMethod_2 - name: 0x600000f52310
AveryTest[56451:20807927] testMethod_3 - array: 0x600000f5d590
AveryTest[56451:20807927] testMethod_4 - array: 0x600000f523d0
AveryTest[56451:20807927] testMethod_4 - array: 0x600000f6adc0
AveryTest[56451:20807927] testMethod_4 - array: 0x600000f5d4a0
objc[56451]: ##############
objc[56451]: AUTORELEASE POOLS for thread 0x700009a7c000
objc[56451]: 3 releases pending.
objc[56451]: [0x7fda1b02d000]  ................  PAGE  (hot) (cold)
objc[56451]: [0x7fda1b02d038]  ################  POOL 0x7fda1b02d038
objc[56451]: [0x7fda1b02d040]    0x600000f52310  __NSCFString
objc[56451]: [0x7fda1b02d048]    0x600000f5d590  __NSArrayM
objc[56451]: ##############



用__weak弱引用的对象什么时候会被释放?看下面这个例子:

{
    /**
     __weak id _reference_short;
     __weak id _reference_long;
     __weak id _reference_array_alloc;
    */
    NSString *str_short = [NSString stringWithFormat:@"Avery"];   // TaggedPointer
    NSString *str_long = [NSString stringWithFormat:@"Avery_iPhone3GS&iPhone5S&iPhoneSE"];  // 被注册到autoreleasepool 
    NSMutableArray *array_0 = [[NSMutableArray alloc] init]; // 不会被注册到autoreleasepool
    // NSMutableArray *array_1 = [NSMutableArray arrayWithObjects:@"Avery", nil];  // 被注册到autoreleasepool
    _reference_short = str_short;
    _reference_long = str_long;  // str_long是一个autorelease对象,设置一个weak的引用来观察它
    _reference_array_alloc = array_0;
    /**
     extern void _objc_autoreleasePoolPrint(void);  // 打印注册到autoreleasePool中的对象
     _objc_autoreleasePoolPrint();
    */
}
TEST[57881:21330279]                     _reference_short (-viewWillAppear-): Avery
TEST[57881:21330279]                      _reference_long (-viewWillAppear-): Avery_iPhone3GS&iPhone5S&iPhoneSE
TEST[57881:21330279]               _reference_array_alloc (-viewWillAppear-): (null)
TEST[57881:21330279]    currentMode (-viewWillAppear-): kCFRunLoopDefaultMode
TEST[57881:21330279] 
TEST[57881:21330279]                     _reference_short (+viewDidAppear+): Avery
TEST[57881:21330279]                      _reference_long (+viewDidAppear+): (null)
TEST[57881:21330279]               _reference_array_alloc (+viewDidAppear+): (null)
TEST[57881:21330279]    currentMode (+viewDidAppear+): kCFRunLoopDefaultMode

猜猜上面的这个弱引用的_reference_long的释放时机是什么时候?


autoreleasepool observers

如上图所示:当App启动后苹果会在主线程的 RunLoop 里注册两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。
第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池、其 order 是-2147483647优先级最高,保证创建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件:
(1) BeforeWaiting(将要进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的autoreleasePool并创建新的autoreleasePool;
(2) Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放autoreleasePool,这个 Observer 的 order 是 2147483647优先级最低,保证其释放池子发生在其他所有回调之后。
此处_reference_long的释放就发生在runloop进入到BeforeWaiting状态时。



通过上面这几个例子你发现了什么?
(1) 当用weak指针修饰的对象调用其方法时、该方法中产生的对象才有可能会被加入到autoreleasePool中去(当方法中的返回值是对象时);
(2) 当用强引用指针修饰的对象调用其方法时(同时又有弱指针与其指向同一块内存)、
该方法中产生的对象有可能会被加入到autoreleasePool中去(当方法中的返回值是对象时);
(3) __weak修饰的指针本身就指向一个autorelease对象时。


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


迷惑行为大赏

在某度搜索"如果大量地使用__weak修饰符的变量,注册到autoreleasepool的对象也会大量增加"、对排在前面的博文进行了截屏:
其它博文截屏

几乎所有的博文都是这样写的或者基本上或者大同小异 (以下代码摘抄于该文章):

{
    //obj是__strong修饰且被赋值的对象
    id __weak obj1 = obj;
    NSLog(@"%@", obj1);
}

// 编译器的模拟代码
id __weak obj1;   // 【原博文中这里少了一个__weak】
objc_initWeak(&obj1, obj);
id tmp = objc_loadWeakRetained(&obj1);
objc_autorelease(tmp);
NSLog(@"%@", obj1);
objc_destroyWeak(&obj1);  // 作用域结束,释放对象

我没记错的话现在都2020年了吧 (或者至少都2020年了)、系统都iOS13.x了。。。所以当你看4 ~ 5年前的帖子的时候请务必多留个心眼!这也是为什么我不让别人直接转载我的博文的原因、如果文章中有错误、我改正了但是谁又能保证转载的文章的质量呢。
美国总统特朗普说喝消毒液可以杀死新冠病毒、你咋不去跟风喝二两呢?
不要人云亦云要留有自己的判断力。
编译后的代码到底是什么样的?到底会不会加入到autoreleasepool中?试一下不就知道了嘛 (环境:XCode 11.3.1 时间:2020.04.30)。。。

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    TestObject *obj = [[TestObject alloc] init];
    [obj testAction];

    /**
     _objc_alloc    // TestObject -> alloc
     _objc_msgSend  // TestObject类alloc出的对象 -> init

     _objc_msgSend  // TestObject类alloc出的对象 -> testAction

     _objc_storeStrong   // 出了作用域、销毁"TestObject类alloc出的对象"
     */
});
{
    NSObject *obj = [[NSObject alloc] init];
    __weak id weakObj = obj;
    NSLog(@"weakObj: %@",weakObj);
    
    /**
     _objc_alloc        // NSObject -> alloc
     _objc_msgSend      // NSObject类alloc出的对象 -> init
     
     _objc_initWeak     // __weak id weakObj = obj;
     
     _objc_loadWeakRetained   // id tmp = objc_loadWeakRetained(&weakObj);  (此函数取出附有__weak修饰符变量所引用的对象并进行retain)
     _NSLog
     _objc_release      // _objc_release(tmp)
     _objc_destroyWeak  // objc_destroyWeak(&weakObj);
     
     _objc_storeStrong  // 出了作用域、销毁obj对象, _objc_storeStrong(&obj);
     _objc_destroyWeak  // 出了作用域、销毁weakObj对象, objc_destroyWeak(&weakObj);
     */
}



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

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