1、AutoReleasePool
在main函数内部添加如下代码:
int main(int argc, const char * argv[]) {@autoreleasepool {Person *person = [[[Person alloc] init] autorelease];}return 0;}
转成c++代码 后:
int main(int argc, const char * argv[]) {{__AtAutoreleasePool __autoreleasepool;Person *person = [[[Person alloc] init] autorelease];}return 0;}
可以发现@autoreleasepool底层是创建了一个AtAutoreleasePool类型的结构体变量,AtAutoreleasePool结构如下:
struct __AtAutoreleasePool {__AtAutoreleasePool() {// 构造函数,在创建结构体的时候调用atautoreleasepoolobj = objc_autoreleasePoolPush();}~__AtAutoreleasePool() {// 析构函数,在结构体销毁的时候调用objc_autoreleasePoolPop(atautoreleasepoolobj);}void * atautoreleasepoolobj;};
内部有构造函数和析构函数,分别调用了push和pop方法,所以可以将main函数里的@autoreleasepool理解成:
atautoreleasepoolobj = objc_autoreleasePoolPush();Person *person = [[[Person alloc] init] autorelease];objc_autoreleasePoolPop(atautoreleasepoolobj);
总结:AutoReleasePool是一个结构体,创建时调用push方法,销毁时调用pop方法。
2、AutoReleasePoolPage
通过查看 objc源码 ,NSObject.mm文件中发现,pop和push方法里面都是操作AutoreleasePoolPage的方法
void *objc_autoreleasePoolPush(void){return AutoreleasePoolPage::push();}NEVER_INLINEvoidobjc_autoreleasePoolPop(void *ctxt){AutoreleasePoolPage::pop(ctxt);}
所以需要了解一下AutoreleasePoolPage,精简过的AutoreleasePoolPage结构如下:
class AutoreleasePoolPage{magic_t const magic;id *next;pthread_t const thread;AutoreleasePoolPage * const parent;AutoreleasePoolPage *child;uint32_t const deptch;uint32_t hiwat;}
每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址,所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起:
3、push和pop
当执行push方法时,会将先POOL_BOUNDARY存入page对象中,并返回其地址。
page其余空间会保存autorelease对象,当一个page不够时,会存入下一个page。
当执行pop方法时,将之前保存的POOL_BOUNDARY地址传入,从最后一个保存的autorelease对象开始释放,直到遇到POOL_BOUNDARY。
@autoreleasepool {// pushfor (int i = 0; i < 1000; i++) {NSObject *obj = [[[NSObject alloc] init] autorelease];}}// pop
执行流程伪代码如下:
atautoreleasepoolobj = objc_autoreleasePoolPush();atautoreleasepoolobj = 0x1038; // POOL_BOUNDARY地址for (int i = 0; i < 1000; i++) {NSObject *obj = [[[NSObject alloc] init] autorelease];}objc_autoreleasePoolPop(0x1038); // POOL_BOUNDARY地址

如果是循环嵌套的就会传入多个autoreleasepool:
@autoreleasepool {// push POOL_BOUNDARY1NSObject *obj = [[[NSObject alloc] init] autorelease];@autoreleasepool {// push POOL_BOUNDARY2NSObject *obj = [[[NSObject alloc] init] autorelease];@autoreleasepool {// push POOL_BOUNDARY3NSObject *obj = [[[NSObject alloc] init] autorelease];}// pop POOL_BOUNDARY3}// pop POOL_BOUNDARY2}// pop POOL_BOUNDARY1
就会依次插入POOL_BOUNDARY并返回地址,在执行pop时,根据传入的POOL_BOUNDARY地址进行释放。
4、查看自动释放池的情况
可以通过以下私有函数来查看自动释放池的情况,先生命私有函数:
extern void _objc_autoreleasePoolPrint(void);
调用并查看打印结果:
@autoreleasepool {NSObject *obj = [[[NSObject alloc] init] autorelease];@autoreleasepool {NSObject *obj = [[[NSObject alloc] init] autorelease];@autoreleasepool {NSObject *obj = [[[NSObject alloc] init] autorelease];_objc_autoreleasePoolPrint();}}}
打印结果:
objc[50538]: ##############objc[50538]: AUTORELEASE POOLS for thread 0x1000ebe00objc[50538]: 6 releases pending.objc[50538]: [0x10680b000] ................ PAGE (hot) (cold)objc[50538]: [0x10680b038] ################ POOL 0x10680b038objc[50538]: [0x10680b040] 0x1007579a0 NSObjectobjc[50538]: [0x10680b048] ################ POOL 0x10680b048objc[50538]: [0x10680b050] 0x100756a00 NSObjectobjc[50538]: [0x10680b058] ################ POOL 0x10680b058objc[50538]: [0x10680b060] 0x100755e50 NSObjectobjc[50538]: ##############Program ended with exit code: 0
通过打印可以看到自动释放池里当前保存了6个内容,分别是插入的三个POOL_BOUNDARY和三个带释放的NSObject对象。
添加多个自动释放对象查看释放情况:
@autoreleasepool {for (int i = 0; i < 1000; i++) {NSObject *obj = [[[NSObject alloc] init] autorelease];}_objc_autoreleasePoolPrint();}
打印结果:
objc[50668]: ##############objc[50668]: AUTORELEASE POOLS for thread 0x1000ebe00objc[50668]: 1001 releases pending.objc[50668]: [0x100810000] ................ PAGE (full) (cold)objc[50668]: [0x100810038] ################ POOL 0x100810038很多autorelease对象地址,省略objc[50668]: [0x10080c000] ................ PAGE (hot)很多autorelease对象地址,省略objc[50668]: ##############Program ended with exit code: 0
由于一个page对象不够存储,所以创建一个新的page,page后面的 (hot) 代表最后一个对象保存在当前页。
5、对象的autorelease方法
NSObject *obj = [[[NSObject alloc] init] autorelease];
可以通过objc源码中的NSObject.mm文件查看实现,最终会调用:
AutoreleasePoolPage::autorelease((id)this);
将对象插入AutoreleasePoolPage中,所以对象调用autorelease方法就是将自己的地址插入AutoreleasePoolPage。
6、autorelease和Runloop
在viewDidLoad方法中添加一个autorelease观察释放情况(MRC环境下):
- (void)viewDidLoad {[super viewDidLoad];Person *person = [[[Person alloc] init] autorelease];NSLog(@"%s", __func__);}- (void)viewWillAppear:(BOOL)animated {[super viewWillAppear:animated];NSLog(@"%s", __func__);}- (void)viewWillDisappear:(BOOL)animated {[super viewWillDisappear:animated];NSLog(@"%s", __func__);}
打印结果:
~: -[ViewController viewDidLoad]~: -[ViewController viewWillAppear:]~: -[Person dealloc]~: -[ViewController viewDidAppear:]
可以看到person并不是在viewdidload方法执行结束时释放的。
autorelease对象释放是由RunLoop控制的,当进入Loop时调用push方法,休眠前调用pop方法和push方法,退出时调用pop方法。
所以person可能是在所在RunLoop循环中休眠前释放的。
