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 && !page->full()) {
return page->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->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->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->add(POOL_BOUNDARY);
}
return page->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->begin();
} else {
page = pageForPointer(token);
}
// 2.
stop = (id *)token;
if (*stop != POOL_BOUNDARY) {
if (stop == page->begin()&&!page->parent) {
} else {
return badPop(token);
}
}
// 3.
return popPage<false>(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->releaseUntil(stop);
if (page->child) {
if (page->lessThanHalfFull()) {
page->child->kill();
}
else if (page->child->child) {
page->child->child->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>-></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->next != stop) {
AutoreleasePoolPage *page = hotPage();
while (page->empty()) {
page = page->parent;
setHotPage(page);
}
page->unprotect();
id obj = *--page->next;
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->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->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->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->parent) {
result = result->parent;
result->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>项目中经常会看到下面的代码,很多人的解释是“这个<code>autoreleasepool</code>是为了释放主线程的<code>autorelease</code>对象的”。但是,这个说法是错误的。<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
<CFRunLoopObserver 0x60000012f000 >{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x10eee6276)}
// activities = 0xa0,kCFRunLoopBeforeWaiting | kCFRunLoopExit
<CFRunLoopObserver 0x60000012ef60 >{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 登录界面布局实现示例详解</li><li>swift语言Codable 用法及原理详解</li><li>IOS开发Swift 与 OC相互调用详解</li><li>swift内存管理指针类型使用实例详解</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]