Autoreleasepool

2020-11-09  本文已影响0人  JerrySi

Autoreleasepool看过好几遍了,每次看完就会忘记,感觉还是要记录下来。

@autoreleasepool

编写一个main.m代码文件,类似于:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"Hello, World!");
    }
    return 0;
}

在终端中使用clang -rewrite-objc main.m命令将上述OC代码重写成C++的实现。生成的代码主要是下面2处:

extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);

struct __AtAutoreleasePool {
 __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
 ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
 void * atautoreleasepoolobj;
};
int main(int argc, const char * argv[]) {

    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_8d_3vn1mhhd5sx58zhlpyzw1wgh0000gn_T_main_171318_mi_0);
    }

    return 0;
}

看到这里,苹果还是很巧妙的:

先调用创建__autoreleasepool对象,自动调用构造方法去Push;然后运行完成,会自动调用析构方法Pop。

POOL_BOUNDARY (苹果这个设计真的很巧妙)

POOL_BOUNDARY的前世叫做POOL_SENTINEL,称为哨兵对象或者边界对象;
POOL_BOUNDARY用来区分不同的自动释放池,以解决自动释放池嵌套的问题;
每当创建一个自动释放池,就会调用push()方法将一个POOL_BOUNDARY入栈,并返回其存放的内存地址;
当往自动释放池中添加autorelease对象时,将autorelease对象的内存地址入栈,它们前面至少有一个POOL_BOUNDARY
当销毁一个自动释放池时,会调用pop()方法并传入一个POOL_BOUNDARY,会从自动释放池中最后一个对象开始,依次给它们发送release消息,直到遇到这个POOL_BOUNDARY

AutoreleasePoolPage

双向链表,每个Page的内存大小为4096个字节,每当Page满了的时候,就会创建一个新的Page。hotPage()方法就是用来获得这个新创建的未满的Page。
autoreleaseFast()在执行过程中有三种情况:

  1. 当前Page存在且未满时,通过page->add(obj)将autorelease对象入栈,即添加到当前Page中;
  2. 当前Page存在但已满时,通过autoreleaseFullPage(obj, page)创建一个新的Page,并将autorelease对象添加进去;
  3. 当前Page不存在,即还没创建过Page,通过autoreleaseNoPage(obj)创建第一个Page,并将autorelease对象添加进去。

释放时机

网上对于iOS工程的main()函数中的@autoreleasepool有一种解释:
在iOS工程的main()函数中有一个@autoreleasepool,这个@autoreleasepool负责了应用程序所有autorelease对象的释放。

其实这个解释是错误的。
如果你的程序使用了AppKit或UIKit框架,那么主线程的RunLoop就会在每次事件循环迭代中创建并处理@autoreleasepool。也就是说,应用程序所有autorelease对象的都是由RunLoop创建的@autoreleasepool来管理。而main()函数中的@autoreleasepool只是负责管理它的作用域中的autorelease对象。

那么子线程中的怎么办呢?
在子线程你创建了 Pool 的话,产生的 Autorelease 对象就会交给 pool 去管理。如果你没有创建 Pool ,但是产生了 Autorelease 对象,就会调用 autoreleaseNoPage 方法。在这个方法中,会自动帮你创建一个 hotpage(hotPage 可以理解为当前正在使用的 AutoreleasePoolPage,如果你还是不理解,可以先看看 Autoreleasepool 的源代码,再来看这个问题 ),并调用 page->add(obj)将对象添加到 AutoreleasePoolPage 的栈中,也就是说你不进行手动的内存管理,也不会内存泄漏啦。

上一篇 下一篇

猜你喜欢

热点阅读