美文网首页
十、自动释放池(AutoreleasePoolPage)与aut

十、自动释放池(AutoreleasePoolPage)与aut

作者: LNG61 | 来源:发表于2018-07-11 20:26 被阅读0次

    一、自动释放池(AutoreleasePoolPage)

    每个工程都有一个main函数入口,通常是这样的:

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

    这里有一个@autoreleasepool,无法一眼看出代表的是什么,可以通过rewrite -objc命令来重写main.m文件,改写后的main函数变成这样的:

    int main(int argc, const char *argv[]){
      /* @autoreleasepool */ {__AtAutoreleasePool __autoreleasepool;
      // other code
      }
    }
    

    可以看出,@autoreleasepool被编译器转为了__AtAutoreleasePool __autoreleasepool__AtAutoreleasePool的定义可以在该文件中找到:

    struct __AtAutoreleasePool{
      __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();
      ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
      void *atautoreleasepoolobj;
    }
    

    __AtAutoreleasePool结构体封装了一个对象atautoreleasepoolobj,构造函数时使用objc_autoreleasePoolPush进行赋值,析构函数调用时使用objc_autoreleasePoolPop进行释放。

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

    这两个函数是对AutoreleasePoolPagepushpop操作进行了一次封装。AutoreleasePoolPage就是我们今天的主角——自动释放池。

    二、AutoreleasePoolPage的实现

    1. AutoreleasePoolPage结构

    class AutoreleasePoolPage{
      magic_t const magic; // 用于校验的字段
      id *next; // 当前存储自动释放对象的指针
      pthread_t const thread; // 当前释放池所在的线程
      AutoreleasePoolPage *const parent; // 前一个节点
      AutoreleasePoolPage *child; // 下一个节点
      uint32_t const depth; // 深度
      uint32_t hitwat;
    }
    

    AutoreleasePoolPage是使用双向链表实现的,如下图:

    战盟图片1531309749.jpg

    2.push的实现

    #define POOL_BOUNDARY nil
    static inline void *push{
      return autoreleaseFast(POOL_BOUNDARY);
    }
    
    static inline id *autoreleaseFast(id obj){
      AutoreleasePoolPage *page = hotPage();
      if(page && !page->full()){
        return page->add(obj);
      }else if(page){
        return autoreleseFullPage(obj, page);
      }else{
        return autoreleaseNoPage(obj);
      }
    }
    

    push主要通过autoreleaseFast来返回一个可用的page

    1. hotPage是指当前页,如果当前页没满的话,那将添加到当前页;
    2. 当前页已满,通过autoreleseFullPage生成一个当前page的下一个链表节点,并添加上去;
    3. 当前页为空,生成第一个链表节点,添加进去。
    

    3.pop的实现

    static inline void pop(void *token){
      AutoreleasePoolPage *page = pageForPointer(token); // 通过指针位移可以获取到token所载的poolPage
      id *stop = (id*)token;
      page->releaseUntil(stop);
      if(page->child){
        if(page->lessThanHalfFull()) page->child->kill();
        else if(page->child->child) page->child->child->kill();
      }
    }
    
    void releaseUntil(id *stop){
       while(this->next != stop){
          AutoreleasePoolPage *page = hotPage();
          while(page->empty()){
            // 找到一个不为空的page
            page = page->parent;
            setHotPage(page);
          }
    
          page->unprotect();
          id obj = *--page->next;
          memset((void *)page->next, SCRIBBLE, sizeof(*page->next));
          page->protect();
          object_release(obj); // 对obj执行release操作
       }
    
        setHotPage(this);
    }
    

    main函数中的临时变量__autoreleasepool,在执行完之后,析构函数会将执行AutoreleasePoolPage:pop(__autoreleasepool),将该变量之后加入自动释放池的所有对象都进行一次release操作,从而达到自动释放的效果。

    4.autorelease

    static int id autorelease(id obj){
       return autoreleaseFast(obj);
    }
    

    autorelease实现跟add一样,将对象加入到自动释放池中。由于ARC下编译器自动帮你确定了调用autorelease的时机,所以不需要显试调用,当然也无法显试调用。

    小结

    1. @autoreleasepool其实就是AutoreleasePoolPage的方便使用入口。
    2. autoreleasepool块执行结束后,会执行pop操作,将这期间所有的autorelease对象释放。

    相关文章

      网友评论

          本文标题:十、自动释放池(AutoreleasePoolPage)与aut

          本文链接:https://www.haomeiwen.com/subject/bukkpftx.html