不贪心不上当 發表於 2022-9-13 14:52:50

swift语言AutoreleasePool原理及使用场景

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>使用场景</li><ul class="second_class_ul"><li>NSAutoreleasePool</li><li>@autoreleasepool</li><li>__autoreleasing</li></ul><li>源码分析</li><ul class="second_class_ul"><li>__AtAutoreleasePool结构体</li><li>AutoreleasePoolPage</li><li>POOL_BOUNDARY</li></ul><li>多层嵌套</li><ul class="second_class_ul"><li>push</li><li>autoreleaseFast</li><li>autoreleaseFullPage</li><li>autoreleaseNoPage</li><li>add</li><li>pop</li><li>popPage</li><li>releaseUntil</li><li>autorelease</li><li>hotPage</li><li>coldPage</li></ul><li>调试</li><ul class="second_class_ul"><li>_objc_autoreleasePoolPrint</li></ul><li>UIApplicationMain</li><ul class="second_class_ul"></ul><li>释放时机</li><ul class="second_class_ul"><li>区分</li><li>runloop</li></ul></ul></div><p class="maodian"></p><h2>使用场景</h2>
<p>在<code>ARC</code>下,<code>AutoreleasePool</code>主要应用在大量创建临时对象的场景,通过<code>AutoreleasePool</code>控制内存峰值,是一个很好的选择。</p>
<p class="maodian"></p><h3>NSAutoreleasePool</h3>
<p>在<code>MRC</code>可以调用<code>NSAutoreleasePool</code>使对象延迟释放,在<code>ARC</code>下这个<code>API</code>已经被禁用。</p>
<div class="jb51code"><pre class="brush:cpp;">NSAutoreleasePool *pool = [ init];
// ...
;
</pre></div>
<p class="maodian"></p><h3>@autoreleasepool</h3>
<p>除了<code>NSAutoreleasePool</code>还可以使用<code>@autoreleasepool</code>,并且苹果推荐使用<code>@autoreleasepool</code>,因为这个<code>API</code>性能更好,在<code>ARC</code>下依然可以使用<code>@autoreleasepool</code>。</p>
<p>无论是<code>MRC</code>还是<code>ARC</code>,<code>autorelease</code>最大的作用,是在大量创建对象的同时,通过修饰让内存得到提前释放,从而降低内存峰值。</p>
<div class="jb51code"><pre class="brush:cpp;">@autoreleasepool {
    NSMutableArray *channelItemsJSONArray = ];
    NSArray *items = ;
    if (! atomically:YES]) {
       atomically:YES];
    }
    items = nil;
}
</pre></div>
<p class="maodian"></p><h3>__autoreleasing</h3>
<p>在<code>ARC</code>下,需要被自动释放的对象,可以用<code>__autoreleasing</code>修饰,让对象延迟释放。</p>
<div class="jb51code"><pre class="brush:cpp;">+ (NSArray *)parseString:(NSString *)originalM3U8Str m3u8Host:(NSString *)m3u8url error:(NSError *__autoreleasing *)errorPtr;
</pre></div>
<p class="maodian"></p><h2>源码分析</h2>
<p class="maodian"></p><h3>__AtAutoreleasePool结构体</h3>
<div class="jb51code"><pre class="brush:cpp;">struct __AtAutoreleasePool {
    __AtAutoreleasePool() {
      atautoreleasepoolobj = objc_autoreleasePoolPush();
    }
    ~__AtAutoreleasePool() {
      objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    void * atautoreleasepoolobj;
};
</pre></div>
<p><code>@autoreleasepool</code>本质上会被系统转换成<code>C++</code>的<code>__AtAutoreleasePool</code>结构体,<code>@autoreleasepool</code>的大括号开始,对应着<code>objc_autoreleasePoolPush</code>函数。大括号结束,对应着<code>objc_autoreleasePoolPop</code>函数。通过<code>clang</code>命令将<code>OC</code>代码转成<code>C++</code>代码,可以看到有一个<code>__AtAutoreleasePool</code>结构体。</p>
<p><code>__AtAutoreleasePool</code>结构体在创建的时候会执行<code>objc_autoreleasePoolPush</code>函数,在释放的时候会执行析构函数,并执行<code>objc_autoreleasePoolPop</code>函数。在这两个函数内部,会调用<code>AutoreleasePoolPage</code>的<code>push</code>和<code>pop</code>函数。</p>
<p class="maodian"></p><h3>AutoreleasePoolPage</h3>
<p>在运行时代码中,<code>objc_autoreleasePoolPop</code>和<code>objc_autoreleasePoolPush</code>,都调用了<code>AutoreleasePoolPage</code>类的实现。</p>
<div class="jb51code"><pre class="brush:cpp;">void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}
</pre></div>
<p>在<code>AutoreleasePoolPage</code>的定义中,可以看到有<code>parent</code>和<code>child</code>的定义,当<code>page</code>中对象太多存储不下时,会创建其他的<code>page</code>对象来存储,<code>AutoreleasePoolPage</code>的结构是一个双向链表。在插入新的<code>autorelease</code>对象时,也会从链表头向后查找,直到找到未满的<code>page</code>。</p>
<div class="jb51code"><pre class="brush:cpp;">class AutoreleasePoolPage
{
    magic_t const magic;                // 校验page的结构是否完整
    id *next;                           // 指向下一个可以存放autorelease对象的地址
    pthread_t const thread;             // 当前所在的线程
    AutoreleasePoolPage * const parent; // 当前page的父节点
    AutoreleasePoolPage *child;         // 当前page的子节点
    uint32_t const depth;               // page的深度
    uint32_t hiwat;
}
</pre></div>
<p><code>AutoreleasePoolPage</code>是一个<code>C++</code>的类,每个<code>page</code>占<code>4096</code>个字节,也就是<code>16</code>进制的<code>0x1000</code>,也就是<code>4kb</code>的空间。这些空间中,其自身的成员变量只占<code>56</code>个字节,也就是下面七个成员变量,每个占<code>8</code>字节,总共<code>56</code>个字节。其他的四千多个字节,都用来存放被<code>autorelease</code>修饰的对象内存地址。</p>
<p class="maodian"></p><h3>POOL_BOUNDARY</h3>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220913084242046.png" /></p>
<p><code>POOL_BOUNDARY</code>的作用是,区分不同的自动释放池,也就是不同的<code>@autoreleasepool</code>。调用<code>push</code>时,会传入<code>POOL_BOUNDARY</code>并返回一个地址例如<code>0x1038</code>,<code>0x1038</code>是不存储<code>@autorelease</code>对象的地址的,起到一个标识作用,用来分割不同的<code>@autoreleasepool</code>。</p>
<p>调用<code>pop</code>时,会传入<code>end</code>的地址,并从后到前调用对象的<code>release</code>方法,直到<code>POOL_BOUNDARY</code>为止。如果存在多个<code>page</code>,会从<code>child</code>的<code>page</code>的最末尾开始调用,直到<code>POOL_BOUNDARY</code>。<code>page</code>的结构是一个栈结构,释放的时候也是从栈顶开始释放。</p>
<p><code>next</code>指针指向栈顶,是栈里面很常见的一个设计。<code>AutoreleasePoolPage</code>和<code>POOL_BOUNDARY</code>的区别在于,<code>AutoreleasePoolPage</code>负责维护存储区域,而<code>POOL_BOUNDARY</code>则负责分割存储在<code>page</code>中的对象地址,以<code>@autoreleasepool</code>为单位进行分割。</p>
<p class="maodian"></p><h2>多层嵌套</h2>
<div class="jb51code"><pre class="brush:cpp;">@autoreleasepool {
    NSObject *p1 = [ init];
    NSObject *p2 = [ init];
    @autoreleasepool {
      NSObject *p3 = [ init];
      @autoreleasepool {
            NSObject *p4 = [ init];
      }
    }
}
</pre></div>
<p>如果是多层<code>@autoreleasepool</code>的嵌套,会用同一个<code>AutoreleasePoolPage</code>对象。以下面的三个嵌套为例,在同一个<code>page</code>中的顺序是下图这样。不同的<code>@autoreleasepool</code>以<code>POOL_BOUNDARY</code>做分割。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220913084242047.png" /></p>
<p class="maodian"></p><h3>push</h3>
<p>创建一个<code>autoreleasePool</code>之后,就会调用<code>push</code>函数。在<code>push</code>函数中会判断是否调试模式下,如果调试模式会每次生成一个新的<code>page</code>。<code>debug</code>环境代码可以直接忽略,只保留<code>autoreleaseFast</code>函数。</p>
<div class="jb51code"><pre class="brush:cpp;">static inline void *push()
{
    id *dest;
    if (DebugPoolAllocation) {
      dest = autoreleaseNewPage(POOL_BOUNDARY);
    } else {
      dest = autoreleaseFast(POOL_BOUNDARY);
    }
    return dest;
}
</pre></div>
<p class="maodian"></p><h3>autoreleaseFast</h3>
<p>在函数内部,会通过<code>hotPage</code>获取当前的<code>page</code>,<code>hotPage</code>函数内部本质上是一个<code>page</code>和<code>key</code>的映射。</p>
<ul><li>如果<code>page</code>不为空并且有空间,则调用<code>page</code>的<code>add</code>函数将对象添加到<code>page</code>中,并将<code>POOL_BOUNDARY</code>添加在当前的位置。</li><li>如果<code>page</code>已经被创建但没有空间,会调用<code>autoreleaseFullPage</code>函数创建新的<code>page</code>,并且将链表的末尾指向新创建的<code>page</code>。</li><li>如果没有创建<code>page</code>,则调用<code>autoreleaseNoPage</code>函数创建一个新的<code>page</code>,并且将当前线程的<code>hotPage</code>设置为新创建的<code>page</code>。</li></ul>
<div class="jb51code"><pre class="brush:cpp;">static inline id *autoreleaseFast(id obj)
{
    AutoreleasePoolPage *page = hotPage();
    if (page &amp;&amp; !page-&gt;full()) {
      return page-&gt;add(obj);
    } else if (page) {
      return autoreleaseFullPage(obj, page);
    } else {
      return autoreleaseNoPage(obj);
    }
}
</pre></div>
<p class="maodian"></p><h3>autoreleaseFullPage</h3>
<ul><li>在<code>autoreleaseFullPage</code>函数中,会从<code>page</code>的链表中,从前往后找到末尾的节点。</li><li>创建一个新的<code>page</code>,在创建函数<code>AutoreleasePoolPage</code>中会处理<code>parent</code>和<code>child</code>指针的问题,返回的<code>page</code>可以直接用。</li><li>调用<code>setHotPage</code>将<code>page</code>设置到哈希表中,并且调用<code>page</code>的<code>add</code>函数将<code>autorelease</code>修饰的对象,添加到<code>page</code>中。</li></ul>
<div class="jb51code"><pre class="brush:cpp;">static __attribute__((noinline))
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
    do {
      if (page-&gt;child) page = page-&gt;child;
      else page = new AutoreleasePoolPage(page);
    } while (page-&gt;full());
    setHotPage(page);
    return page-&gt;add(obj);
}
</pre></div>
<p class="maodian"></p><h3>autoreleaseNoPage</h3>
<p><code>autoreleaseNoPage</code>函数的核心代码比较简单,就是创建一个新的<code>page</code>,随后设置<code>POOL_BOUNDARY</code>标志,并且把对象添加进去。在函数中需要留意<code>POOL_BOUNDARY</code>标志,很多地方都用来做<code>page</code>是否为空的判断。</p>
<div class="jb51code"><pre class="brush:cpp;">static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
    AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
    setHotPage(page);
    if (pushExtraBoundary) {
      page-&gt;add(POOL_BOUNDARY);
    }
    return page-&gt;add(obj);
}
</pre></div>
<p class="maodian"></p><h3>add</h3>
<p><code>add</code>函数比较简单,核心逻辑就是将<code>obj</code>放入<code>next</code>指针的位置,并且对<code>next</code>指针进行<code>++</code>,指向下一个位置。<code>*next++</code>表示先用后加,先将<code>obj</code>存入next的地址,随后<code>+1</code>。</p>
<div class="jb51code"><pre class="brush:cpp;">id *add(id obj)
{
    ASSERT(!full());
    unprotect();
    id *ret = next;
    *next++ = obj;
    protect();
    return ret;
}
</pre></div>
<p class="maodian"></p><h3>pop</h3>
<p>调用<code>pop</code>函数时,有三步处理。</p>
<ul><li>判断<code>autoreleasepool</code>是否为空,通过<code>EMPTY_POOL_PLACEHOLDER</code>占位符判断,为空则清空这个<code>page</code>。</li><li>传入的<code>stop</code>是否不等于<code>POOL_BOUNDARY</code>标识,如果不等于则可能是一个有问题的<code>page</code>。</li><li>调用<code>popPage</code>方法,释放对象。</li></ul>
<div class="jb51code"><pre class="brush:cpp;">static inline void
pop(void *token)
{
    AutoreleasePoolPage *page;
    id *stop;
    // 1.
    if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
      page = hotPage();
      if (!page) {
            return setHotPage(nil);
      }
      page = coldPage();
      token = page-&gt;begin();
    } else {
      page = pageForPointer(token);
    }
    // 2.
    stop = (id *)token;
    if (*stop != POOL_BOUNDARY) {
      if (stop == page-&gt;begin()&amp;&amp;!page-&gt;parent) {
      } else {
            return badPop(token);
      }
    }
    // 3.
    return popPage&lt;false&gt;(token, page, stop);
}
</pre></div>
<p class="maodian"></p><h3>popPage</h3>
<p><code>popPage</code>函数核心代码就是调用<code>releaseUntil</code>函数,在最开始会调用<code>releaseUntil</code>函数去完成释放操作。</p>
<p>按照<code>page</code>达到一半就扩容的原则,后面的<code>if</code>语句会判断执行<code>pop</code>后<code>page</code>链表的状态。</p>
<p>如果少于半满,就将子节点删除。</p>
<p>如果大于半满,则保留子节点,并删除后面的节点。</p>
<div class="jb51code"><pre class="brush:cpp;">static void
popPage(void *token, AutoreleasePoolPage *page, id *stop)
{
    page-&gt;releaseUntil(stop);
    if (page-&gt;child) {
      if (page-&gt;lessThanHalfFull()) {
            page-&gt;child-&gt;kill();
      }
      else if (page-&gt;child-&gt;child) {
            page-&gt;child-&gt;child-&gt;kill();
      }
    }
}
</pre></div>
<p class="maodian"></p><h3>releaseUntil</h3>
<p>在<code>releaseUntil</code>函数内部,核心逻辑是从当前<code>page</code>,从后到前调用<code>objc_release</code>,释放被<code>autorelease</code>修饰的对象。</p>
<ul><li>获取当前的<code>hotPage</code>。</li><li>判断<code>page</code>是否为空,如果为空则表示里面的对象被释放完,则将<code>page</code>的父节点<code>page</code>设置为<code>hotPage</code>。</li><li>获得上一个节点,<code>-&gt;</code>的算数优先级比<code>--</code>要高,所以是先通过<code>next</code>获取当前节点地址,这是一个为空的待存入节点,随后执行<code>--</code>操作获取上一个对象地址。</li><li>通过<code>memset</code>将上一个节点释放。</li><li>判断上一个节点是否占位符号<code>POOL_BOUNDARY</code>,如果不是则调用<code>objc_release</code>释放对象。</li><li>在<code>while</code>循环结束后,将当前<code>page</code>设置为<code>hotPage</code>。</li></ul>
<div class="jb51code"><pre class="brush:cpp;">void releaseUntil(id *stop)
{
    while (this-&gt;next != stop) {
      AutoreleasePoolPage *page = hotPage();
      while (page-&gt;empty()) {
            page = page-&gt;parent;
            setHotPage(page);
      }
      page-&gt;unprotect();
      id obj = *--page-&gt;next;
      memset((void*)page-&gt;next, SCRIBBLE, sizeof(*page-&gt;next));
      page-&gt;protect();
      if (obj != POOL_BOUNDARY) {
            objc_release(obj);
      }
    }
    setHotPage(this);
}
</pre></div>
<p class="maodian"></p><h3>autorelease</h3>
<p>对象调用<code>autorelease</code>方法会被编译器转换为<code>objc_autoreleaseReturnValue</code>方法,并且经过多层调用,会来到底层的<code>autorelease</code>函数。</p>
<p>在这个函数中会判断传入的对象是否<code>tagged pointer</code>,因为<code>tagged pointer</code>没有引用计数的概念。随后会调用<code>autoreleaseFast</code>函数,函数内部调用<code>add</code>函数将<code>obj</code>对象加入到<code>page</code>中,并且会判断是否需要创建新的<code>page</code>。</p>
<div class="jb51code"><pre class="brush:cpp;">static inline id autorelease(id obj)
{
    assert(!obj-&gt;isTaggedPointer());
    id *dest __unused = autoreleaseFast(obj);
    return obj;
}
</pre></div>
<p class="maodian"></p><h3>hotPage</h3>
<p><code>hotPage</code>可以被理解为,<code>page</code>链表的末尾,也就是调用<code>push</code>函数被插入的位置。执行<code>hotPage</code>函数获取,以及调用<code>setHotPage</code>设置,都是操作的链表的末尾<code>page</code>。</p>
<p><code>AutoreleasePoolPage</code>对象和线程一一对应,并且都被存储在<code>tls</code>的哈希表中。通过<code>tls_get_direct</code>函数并传入<code>key</code>可以获取到对应的自动释放池。</p>
<div class="jb51code"><pre class="brush:cpp;">static inline AutoreleasePoolPage *hotPage()
{
    AutoreleasePoolPage *result = (AutoreleasePoolPage *)
      tls_get_direct(key);
    if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
    if (result) result-&gt;fastcheck();
    return result;
}
</pre></div>
<p><code>hotPage</code>函数中的判断是下面的定义,这个标示意思是当前<code>page</code>为空,也就是从未存储过任何对象。是一个标志位,下面是标志位的定义。</p>
<div class="jb51code"><pre class="brush:cpp;"># define EMPTY_POOL_PLACEHOLDER ((id*)1)
</pre></div>
<p class="maodian"></p><h3>coldPage</h3>
<p><code>coldPage</code>只有获取函数,没有设置函数。这是因为<code>coldPage</code>函数本质上,就是寻找<code>page</code>链表的根节点,从源码中的<code>while</code>循环可以看到。</p>
<div class="jb51code"><pre class="brush:cpp;">static inline AutoreleasePoolPage *coldPage()
{
    AutoreleasePoolPage *result = hotPage();
    if (result) {
      while (result-&gt;parent) {
            result = result-&gt;parent;
            result-&gt;fastcheck();
      }
    }
    return result;
}
</pre></div>
<p class="maodian"></p><h2>调试</h2>
<p class="maodian"></p><h3>_objc_autoreleasePoolPrint</h3>
<p>如果想调试自动释放池,可以通过<code>_objc_autoreleasePoolPrint</code>私有<code>API</code>来进行。将项目改为<code>MRC</code>,并且在命令行项目中增加下面这些调试代码。</p>
<div class="jb51code"><pre class="brush:cpp;">int main(int argc, const char * argv[]) {
    _objc_autoreleasePoolPrint();   // print1
    @autoreleasepool {
      _objc_autoreleasePoolPrint(); // print2
      Person *p1 = [[ init] autorelease];
      Person *p2 = [[ init] autorelease];
      _objc_autoreleasePoolPrint(); // print3
    }
    _objc_autoreleasePoolPrint();   // print4
    return 0;
}
</pre></div>
<p>打印结果如下,可以看到<code>POOL_BOUNDARY</code>在<code>page</code>中也占了一个位置。</p>
<div class="jb51code"><pre class="brush:cpp;">objc: ############## (print1)
objc: AUTORELEASE POOLS for thread 0x1000aa5c0
objc: 0 releases pending. // 当前自动释放池中没有任何对象
objc: ................PAGE(hot) (cold)
objc: ##############
objc: ############## (print2)
objc: AUTORELEASE POOLS for thread 0x1000aa5c0
objc: 1 releases pending. // 当前自动释放池中有1个对象,这个对象为POOL_BOUNDARY
objc: ................PAGE(hot) (cold)
objc: ################POOL 0x102802038//POOL_BOUNDARY
objc: ##############
objc: ############## (print3)
objc: AUTORELEASE POOLS for thread 0x1000aa5c0
objc: 3 releases pending. // 当前自动释放池中有3个对象
objc: ................PAGE(hot) (cold)
objc: ################POOL 0x102802038//POOL_BOUNDARY
objc:        0x100704a10HTPerson          //p1
objc:        0x10075cc30HTPerson          //p2
objc: ##############
objc: ############## (print4)
objc: AUTORELEASE POOLS for thread 0x1000aa5c0
objc: 0 releases pending. // 当前自动释放池中没有任何对象,因为@autoreleasepool作用域结束,调用pop方法释放了对象
objc: ................PAGE(hot) (cold)
objc: ##############
</pre></div>
<p class="maodian"></p><h2>UIApplicationMain</h2>
<p>项目中经常会看到下面的代码,很多人的解释是&ldquo;这个<code>autoreleasepool</code>是为了释放主线程的<code>autorelease</code>对象的&rdquo;。但是,这个说法是错误的。<code>autoreleasepool</code>只负责自己作用域中添加的对象,而主线程在运行过程中,也会隐式创建<code>autoreleasepool</code>对象,这个<code>pool</code>是包含在<code>main</code>函数的<code>pool</code>里面的。</p>
<p>所以,主线程<code>runloop</code>每次执行循环后,释放的对象是主线程的。而<code>main</code>函数的<code>autoreleasepool</code>释放的,是<code>main</code>函数中直接创建的对象。</p>
<div class="jb51code"><pre class="brush:cpp;">int main(int argc, char * argv[]) {
    @autoreleasepool {
      return UIApplicationMain(argc, argv, nil, NSStringFromClass());
    }
}
</pre></div>
<p class="maodian"></p><h2>释放时机</h2>
<p class="maodian"></p><h3>区分</h3>
<p>如果是在<code>viewDidLoad</code>方法中创建一个<code>autorelease</code>对象,并不是在这个方法结束后释放对象,这个说法是错误的。即便执行到<code>viewDidAppear</code>,依然不会释放对象。</p>
<p>被<code>autorelease</code>修饰的对象,释放时机有两种。</p>
<ul><li>如果通过代码添加一个<code>autoreleasepool</code>,在作用域结束时,随着<code>pool</code>的释放,就会释放<code>pool</code>中的对象。这种情况是及时释放的,并不依赖于<code>runloop</code>。</li><li>另一种就是由系统自动进行释放,系统会在<code>runloop</code>开始的时候创建一个<code>pool</code>,结束的时候会对<code>pool</code>中的对象执行<code>release</code>操作。</li></ul>
<p class="maodian"></p><h3>runloop</h3>
<p>如果是系统创建的<code>pool</code>,需要手动开启<code>runloop</code>,主线程默认已经开启并运行,子线程需要调用<code>currentRunLoop</code>方法开启并运行<code>runloop</code>,子线程中系统创建<code>pool</code>的流程才会正常工作。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220913084242048.png" /></p>
<p>包括主线程在内的每个线程,如果在线程中使用到了<code>AutoreleasePool</code>,则会创建两个<code>Observer</code>并添加到当前线程的<code>Runloop</code>中,通过这两个<code>Observer</code>进行对象的自动内存管理。</p>
<div class="jb51code"><pre class="brush:cpp;">// activities = 0x1,kCFRunLoopEntry
&lt;CFRunLoopObserver 0x60000012f000 &gt;{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x10eee6276)}
// activities = 0xa0,kCFRunLoopBeforeWaiting | kCFRunLoopExit
&lt;CFRunLoopObserver 0x60000012ef60 &gt;{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x10eee6276)}
</pre></div>
<p>首先会创建一个<code>Observer</code>并监听<code>kCFRunLoopEntry</code>消息,时机是在进入<code>Runloop</code>前,此<code>Observer</code>的优先级设置为<code>-2147483647</code>的最高优先级,以保证回调发生在<code>Runloop</code>其他事件前。</p>
<p>然后创建另一个<code>Observer</code>,并监听<code>kCFRunLoopBeforeWaiting</code>和<code>kCFRunLoopExit</code>消息,时机分别在进入<code>Runloop</code>休眠和退出<code>Runloop</code>时,将<code>Observer</code>的优先级设置为<code>2147483647</code>,以保证回调发生在<code>Runloop</code>其他事件之后。</p>
<p>两个<code>Observer</code>都有相同的回调函数<code>_wrapRunLoopWithAutoreleasePoolHandler</code>,在第一次回调时会在内部调用<code>_objc_autoreleasePoolPush</code>函数,创建自动释放池。</p>
<p>在<code>kCFRunLoopBeforeWaiting</code>将要进入休眠前,调用<code>_objc_autoreleasePoolPop</code>函数释放自动释放池中的对象,并调用<code>_objc_autoreleasePoolPush</code>函数创建一个新的释放池。在<code>kCFRunLoopExit</code>将要退出<code>Runloop</code>时调用<code>_objc_autoreleasePoolPop</code>函数,释放自动释放池中的对象。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220913084242049.png" /></p>
<p>以上就是swift语言AutoreleasePool原理及使用场景的详细内容,更多关于swift AutoreleasePool 原理的资料请关注琼殿技术社区其它相关文章!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>swiftui开发之padding默认值设置详解</li><li>SwiftUI 引导页界面实现示例</li><li>SwiftUI&nbsp;登录界面布局实现示例详解</li><li>swift语言Codable 用法及原理详解</li><li>IOS开发Swift 与 OC相互调用详解</li><li>swift内存管理指针类型使用实例详解</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: swift语言AutoreleasePool原理及使用场景