1. autorelease对象释放时机?

    在当前runloop循环结束的时候,runloop会执行objc_autoreleasePoolPop(context),这个操作才是真正释放autorelease对象的操作(具体实现后面会讲到)。或者是手动的@autoreleasepool{}结束之后,因为编译器会在@autoreleasepool{}的开始跟结束自动加上push跟pop。

    1
    2
    3
    4
    5
    struct __AtAutoreleasePool {
    __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
    ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
    void * atautoreleasepoolobj;
    };
  2. autorelease原理

autoreleasePool本身是一个双向链表,保存的元素是autoreleasePoolPage对象。
autoreleasePoolPage
(通过其中的parent跟child就能看出整个autoreleasePool是个双向链表)
每个autorelease对象会push到poolpage中,next指针指向的是栈顶位置也就是下一个可以存放autorelease对象的位置:

当调用 [obj autorelease]时,就会把对象放入next的位置。当page满了之后,会开辟一个新的page。
每次调用objc_autoreleasePoolPush时,会插入一个哨兵对象(POOL_SENTINEL)到栈顶:

哨兵对象其实就是一个nil

1
#define POOL_SENTINEL nil

当释放autoreleasepool时(即调用objc_autoreleasePoolPop(哨兵对象)),则会依次调用哨兵对象后面对象的release方法,从而实现对象的释放。释放结束之后page就变成:

  1. 系统如何实现在runloop一次迭代结束时执行objc_autoreleasePoolPop释放autoreleasePool的?

    主runloop启动时,系统会add两个observer,分别监听runloop的启动状态(kCFRunLoopEntry)跟即将休眠和退出态(kCFRunLoopBeforeWaiting | kCFRunLoopExit)。在启动的observer回调中执行objc_autoreleasePoolPush操作,在休眠或退出时执行objc_autoreleasePoolPop。

  2. 因为主runloop是在主线程中,那子线程的autorelease对象怎么办?

    autoreleasePoolPage内部有一个thread变量来表明当前page所在对象,并且在工作线程中打印当前runloop会发现当前runloop并没有类似的observer来监听runloop的状态,所以可以猜测:
    所有线程的autoreleasePoolPage都保存在一个双向链表中,所有线程的autorelease对象都会保存在这个链表中对应线程的page中,这个双向链表由主runloop统一管理(只是一个猜测,没有找到相关的介绍,所以如果哪位了解这块的话跪求讲解)


参考:

  1. sunnyxx | 黑幕背后的Autorelease
  2. Haley_Wong | RunLoop总结:RunLoop 与GCD 、Autorelease Pool之间的关系
  3. Draveness | 自动释放池的前世今生 —— 深入解析 Autoreleasepool