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);
     */
}



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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。