1、AutoReleasePool

在main函数内部添加如下代码:

  1. int main(int argc, const char * argv[]) {
  2. @autoreleasepool {
  3. Person *person = [[[Person alloc] init] autorelease];
  4. }
  5. return 0;
  6. }

转成c++代码 后:

  1. int main(int argc, const char * argv[]) {
  2. {
  3. __AtAutoreleasePool __autoreleasepool;
  4. Person *person = [[[Person alloc] init] autorelease];
  5. }
  6. return 0;
  7. }

可以发现@autoreleasepool底层是创建了一个AtAutoreleasePool类型的结构体变量,AtAutoreleasePool结构如下:

  1. struct __AtAutoreleasePool {
  2. __AtAutoreleasePool() {// 构造函数,在创建结构体的时候调用
  3. atautoreleasepoolobj = objc_autoreleasePoolPush();
  4. }
  5. ~__AtAutoreleasePool() {// 析构函数,在结构体销毁的时候调用
  6. objc_autoreleasePoolPop(atautoreleasepoolobj);
  7. }
  8. void * atautoreleasepoolobj;
  9. };

内部有构造函数和析构函数,分别调用了push和pop方法,所以可以将main函数里的@autoreleasepool理解成:

  1. atautoreleasepoolobj = objc_autoreleasePoolPush();
  2. Person *person = [[[Person alloc] init] autorelease];
  3. objc_autoreleasePoolPop(atautoreleasepoolobj);

总结:AutoReleasePool是一个结构体,创建时调用push方法,销毁时调用pop方法。

2、AutoReleasePoolPage

通过查看 objc源码 ,NSObject.mm文件中发现,pop和push方法里面都是操作AutoreleasePoolPage的方法

  1. void *
  2. objc_autoreleasePoolPush(void)
  3. {
  4. return AutoreleasePoolPage::push();
  5. }
  6. NEVER_INLINE
  7. void
  8. objc_autoreleasePoolPop(void *ctxt)
  9. {
  10. AutoreleasePoolPage::pop(ctxt);
  11. }

所以需要了解一下AutoreleasePoolPage,精简过的AutoreleasePoolPage结构如下:

  1. class AutoreleasePoolPage
  2. {
  3. magic_t const magic;
  4. id *next;
  5. pthread_t const thread;
  6. AutoreleasePoolPage * const parent;
  7. AutoreleasePoolPage *child;
  8. uint32_t const deptch;
  9. uint32_t hiwat;
  10. }

每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址,所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起:
image.png

3、push和pop

当执行push方法时,会将先POOL_BOUNDARY存入page对象中,并返回其地址。
page其余空间会保存autorelease对象,当一个page不够时,会存入下一个page。
当执行pop方法时,将之前保存的POOL_BOUNDARY地址传入,从最后一个保存的autorelease对象开始释放,直到遇到POOL_BOUNDARY。

  1. @autoreleasepool {// push
  2. for (int i = 0; i < 1000; i++) {
  3. NSObject *obj = [[[NSObject alloc] init] autorelease];
  4. }
  5. }// pop

执行流程伪代码如下:

  1. atautoreleasepoolobj = objc_autoreleasePoolPush();
  2. atautoreleasepoolobj = 0x1038; // POOL_BOUNDARY地址
  3. for (int i = 0; i < 1000; i++) {
  4. NSObject *obj = [[[NSObject alloc] init] autorelease];
  5. }
  6. objc_autoreleasePoolPop(0x1038); // POOL_BOUNDARY地址

image.png
如果是循环嵌套的就会传入多个autoreleasepool:

  1. @autoreleasepool {// push POOL_BOUNDARY1
  2. NSObject *obj = [[[NSObject alloc] init] autorelease];
  3. @autoreleasepool {// push POOL_BOUNDARY2
  4. NSObject *obj = [[[NSObject alloc] init] autorelease];
  5. @autoreleasepool {// push POOL_BOUNDARY3
  6. NSObject *obj = [[[NSObject alloc] init] autorelease];
  7. }// pop POOL_BOUNDARY3
  8. }// pop POOL_BOUNDARY2
  9. }// pop POOL_BOUNDARY1

就会依次插入POOL_BOUNDARY并返回地址,在执行pop时,根据传入的POOL_BOUNDARY地址进行释放。
image.png

4、查看自动释放池的情况

可以通过以下私有函数来查看自动释放池的情况,先生命私有函数:

  1. extern void _objc_autoreleasePoolPrint(void);

调用并查看打印结果:

  1. @autoreleasepool {
  2. NSObject *obj = [[[NSObject alloc] init] autorelease];
  3. @autoreleasepool {
  4. NSObject *obj = [[[NSObject alloc] init] autorelease];
  5. @autoreleasepool {
  6. NSObject *obj = [[[NSObject alloc] init] autorelease];
  7. _objc_autoreleasePoolPrint();
  8. }
  9. }
  10. }

打印结果:

  1. objc[50538]: ##############
  2. objc[50538]: AUTORELEASE POOLS for thread 0x1000ebe00
  3. objc[50538]: 6 releases pending.
  4. objc[50538]: [0x10680b000] ................ PAGE (hot) (cold)
  5. objc[50538]: [0x10680b038] ################ POOL 0x10680b038
  6. objc[50538]: [0x10680b040] 0x1007579a0 NSObject
  7. objc[50538]: [0x10680b048] ################ POOL 0x10680b048
  8. objc[50538]: [0x10680b050] 0x100756a00 NSObject
  9. objc[50538]: [0x10680b058] ################ POOL 0x10680b058
  10. objc[50538]: [0x10680b060] 0x100755e50 NSObject
  11. objc[50538]: ##############
  12. Program ended with exit code: 0

通过打印可以看到自动释放池里当前保存了6个内容,分别是插入的三个POOL_BOUNDARY和三个带释放的NSObject对象。
添加多个自动释放对象查看释放情况:

  1. @autoreleasepool {
  2. for (int i = 0; i < 1000; i++) {
  3. NSObject *obj = [[[NSObject alloc] init] autorelease];
  4. }
  5. _objc_autoreleasePoolPrint();
  6. }

打印结果:

  1. objc[50668]: ##############
  2. objc[50668]: AUTORELEASE POOLS for thread 0x1000ebe00
  3. objc[50668]: 1001 releases pending.
  4. objc[50668]: [0x100810000] ................ PAGE (full) (cold)
  5. objc[50668]: [0x100810038] ################ POOL 0x100810038
  6. 很多autorelease对象地址,省略
  7. objc[50668]: [0x10080c000] ................ PAGE (hot)
  8. 很多autorelease对象地址,省略
  9. objc[50668]: ##############
  10. Program ended with exit code: 0

由于一个page对象不够存储,所以创建一个新的page,page后面的 (hot) 代表最后一个对象保存在当前页。

5、对象的autorelease方法

  1. NSObject *obj = [[[NSObject alloc] init] autorelease];

可以通过objc源码中的NSObject.mm文件查看实现,最终会调用:

  1. AutoreleasePoolPage::autorelease((id)this);

将对象插入AutoreleasePoolPage中,所以对象调用autorelease方法就是将自己的地址插入AutoreleasePoolPage。

6、autorelease和Runloop

在viewDidLoad方法中添加一个autorelease观察释放情况(MRC环境下):

  1. - (void)viewDidLoad {
  2. [super viewDidLoad];
  3. Person *person = [[[Person alloc] init] autorelease];
  4. NSLog(@"%s", __func__);
  5. }
  6. - (void)viewWillAppear:(BOOL)animated {
  7. [super viewWillAppear:animated];
  8. NSLog(@"%s", __func__);
  9. }
  10. - (void)viewWillDisappear:(BOOL)animated {
  11. [super viewWillDisappear:animated];
  12. NSLog(@"%s", __func__);
  13. }

打印结果:

  1. ~: -[ViewController viewDidLoad]
  2. ~: -[ViewController viewWillAppear:]
  3. ~: -[Person dealloc]
  4. ~: -[ViewController viewDidAppear:]

可以看到person并不是在viewdidload方法执行结束时释放的。
autorelease对象释放是由RunLoop控制的,当进入Loop时调用push方法,休眠前调用pop方法和push方法,退出时调用pop方法。
image.png
所以person可能是在所在RunLoop循环中休眠前释放的。