一、 @autoreleasepool{}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
    }
    return 0;
}

咱们平时创立一个main函数的代码的时分,就会发现其中有一个这个东西@autoreleasepool{},运用clang编译之后:@autoreleasepool{...}被编译成了{__AtAutoreleasePool __autoreleasepool; ... }

这个__AtAutoreleasePool到底是什么?

它其实是一个结构体,在创立__AtAutoreleasePool结构体变量的时分调用了objc_autoreleasePoolPush(void),毁掉的时分会调动objc_autoreleasePoolPop(void *),其实结构函数和析构函数,所以咱们能够看出其其实是一个C++封装的主动开释池变量,会将@autoreleasepool{…}中{}中的内容增加到主动开释池中,方便内存管理。

struct __AtAutoreleasePool {
    // 结构函数
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
    // 析构函数
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

二、AutoreleasePoolPage

从上边的__AtAutoreleasePool咱们能够看到这两个办法objc_autoreleasePoolPushobjc_autoreleasePoolPop

void *objc_autoreleasePoolPush(void) {
    return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt) {
    AutoreleasePoolPage::pop(ctxt);
}

咱们能够看出这里又引入了新的类AutoreleasePoolPage

class AutoreleasePoolPage {
    magic_t const magic;//AutoreleasePoolPage 完整性校验
    id *next;//存放下一个autorelease目标的地址
    pthread_t const thread; //AutoreleasePoolPage 所在的线程
    AutoreleasePoolPage * const parent;//父节点
    AutoreleasePoolPage *child;//子节点
    uint32_t const depth;//深度,也能够理解为当前page在链表中的方位
    uint32_t hiwat;
}

每一个主动开释池都是由一系列AutoreleasePoolPage组成的,而且每一个AutoreleasePoolPage的巨细都是4096字节(16 进制 0x1000)。

所以咱们从上述的源码能够看出,主动开释池其实便是一个由AutoreleasePoolPage构成的双向链表,其结构中的childparent别离指向其前趋和后继。

其中有 56 bit 用于存储AutoreleasePoolPage的成员变量,剩余的0x100816038 ~ 0x100817000都是用来存储加入到主动开释池中的目标。

iOS深入理解Autoreleasepool(自动释放池)

  • 调用push办法会将一个POOL_BOUNDARY入栈,而且返回其存放的内存地址

  • 调用pop办法时传入一个POOL_BOUNDARY的内存地址,会从最终一个入栈的目标开始发送release音讯,直到遇到这个POOL_BOUNDARY

  • id *next指向了下一个能存放autorelease目标地址的区域

三、Runloop和Autorelease

AutoreleasePool创立

  • App启动后,苹果在主线程RunLoop里注册了两个Observer,其回调都是_wrapRunLoopWithAutoreleasePoolHandler()
  • 第一个Observer监督的事情是Entry(行将进入Loop),其回调内会调用
    _objc_autoreleasePoolPush() 创立主动开释池。其 order 是-2147483647,优先级最高,保证创立开释池产生在其他一切回调之前。

AutoreleasePool开释

  • 第二个Observer监督了两个事情: BeforeWaiting(准备进入休眠)
    时调用_objc_autoreleasePoolPop()_objc_autoreleasePoolPush()开释旧的池并创立新池;Exit(行将退出Loop)时调用 _objc_autoreleasePoolPop()来开释主动开释池。这个Observer 的 order 是 2147483647,优先级最低,保证其开释池子产生在其他一切回调之后。
  • 在主线程执行的代码,通常是写在比如事情回调、Timer回调内的。这些回调会被 RunLoop 创立好的AutoreleasePool环绕着,所以不会呈现内存泄漏。

四、面试题AutoreleasePool的原理

  • 主动开释池的实质是一个AutoreleasePoolPage结构体目标,是一个栈结构存储的页,每一个AutoreleasePoolPage都是以双向链表的方式连接

  • 主动开释池的压栈出栈主要是通过结构体的结构函数和析构函数调用底层的objc_autoreleasePoolPushobjc_autoreleasePoolPop,实际上是调用AutoreleasePoolPagepushpop两个办法。

  • 调用push操作其实便是创立一个新的AutoreleasePoolPage,而AutoreleasePoolPage的具体操作便是插入一个岗兵POOL_BOUNDARY,并返回插入岗兵POOL_BOUNDARY的内存地址。而push内部调用autoreleaseFast办法处理,主要有以下三种状况:

    • a.当page存在,且不满时,调用add办法将目标增加至pagenext指针处,并将next指向下一位;
    • b.当page存在,且已满时,调用autoreleaseFullPage初始化一个新的page,然后调用add办法将目标增加至page栈中;
    • c.当page不存在时,调用autoreleaseNoPage创立一个hotPage,然后调用add办法将目标增加至page栈中。
  • 调用pop操作时,会传入一个值,这个值便是push操作的返回值,即岗兵POOL_BOUNDARY内存地址token。所以pop内部的实现便是依据token找到岗兵目标所处的page中,然后运用 objc_release 开释 token之前的目标,并把next 指针到正确方位。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。