iOS 子线程的自动释放池

都说子线程不主动获取没有runloop,而每个runloop都会在开始创建一个自动释放池,那没有runloop,子线程是否有自动释放池呢?比如下面这个

image.png

那么,

  1. 子线程是否有自动释放池呢
  2. 这个obj究竟在什么时候释放呢?
  3. 是在子线程销毁后释放,还是与子线程的生命周期无关?

接下来让我们带着疑问一起去看下runtime源码一探究竟吧。

附可编译源码地址

autoreleasepool源码在NSObject.mm文件中。我们在main.m中添加如下代码,并将main.m编译成main.cpp文件,看下到底发生了什么。

#import 
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            __autoreleasing NSObject *a = [NSObject new];
        });
        while (1) {
            
        }
    }
    return 0;
}
  1. cd 到main.m目录
  2. clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations main.m

这个时候我们会发现在main.m同级目录出现了一个main.cpp文件,打开此文件,移动到文件底部

image.png

接下来假设你已经知道了autoreleasePool的相关知识,不知道的可以看一下这里

首先,在你创建一个autorelease对象的时候,
会调用对象的autorelease方法,这个方法目前在NSObject.h中是没有声明的,不过我们可以看NSObject.mm方法

1.调用autorelease

2.调用到_objc_rootAutorelease

3.调用obj->rootAutorelease

。。。

最终,会调用到autoreleaseFast方法:

这里我们可以注意下hotPage()方法

这里的获取的autoreleasePoolPage是调用的tls_get_direct方法去获取

static inline void *tls_get_direct(tls_key_t k)
{ 
    ASSERT(is_valid_direct_key(k));

    if (_pthread_has_direct_tsd()) {
        return _pthread_getspecific_direct(k);
    } else {
        return pthread_getspecific(k);
    }
}
static inline void tls_set_direct(tls_key_t k, void *value) 
{ 
    ASSERT(is_valid_direct_key(k));

    if (_pthread_has_direct_tsd()) {
        _pthread_setspecific_direct(k, value);
    } else {
        pthread_setspecific(k, value);
    }
}

这两个函数相当于NSObject的动态关联对象,可以将数据绑定到线程上。那么很明显,这个时候子线程是不可能有hotpage,所以这个时候会走到autoreleaseNoPage方法,

    id *autoreleaseNoPage(id obj)
    {
        // "No page" could mean no pool has been pushed
        // or an empty placeholder pool has been pushed and has no contents yet
        ASSERT(!hotPage());

        bool pushExtraBoundary = false;
        if (haveEmptyPoolPlaceholder()) {
            // We are pushing a second pool over the empty placeholder pool
            // or pushing the first object into the empty placeholder pool.
            // Before doing that, push a pool boundary on behalf of the pool 
            // that is currently represented by the empty placeholder.
            pushExtraBoundary = true;
        }
        else if (obj != POOL_BOUNDARY  &&  DebugMissingPools) {
            // We are pushing an object with no pool in place, 
            // and no-pool debugging was requested by environment.
            _objc_inform("MISSING POOLS: (%p) Object %p of class %s "
                         "autoreleased with no pool in place - "
                         "just leaking - break on "
                         "objc_autoreleaseNoPool() to debug", 
                         objc_thread_self(), (void*)obj, object_getClassName(obj));
            objc_autoreleaseNoPool(obj);
            return nil;
        }
        else if (obj == POOL_BOUNDARY  &&  !DebugPoolAllocation) {
            // We are pushing a pool with no pool in place,
            // and alloc-per-pool debugging was not requested.
            // Install and return the empty pool placeholder.
            return setEmptyPoolPlaceholder();
        }

        // We are pushing an object or a non-placeholder'd pool.

        // Install the first page.
        AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
        setHotPage(page);
        
        // Push a boundary on behalf of the previously-placeholder'd pool.
        if (pushExtraBoundary) {
            page->add(POOL_BOUNDARY);
        }
        
        // Push the requested object or pool.
        return page->add(obj);
    }

通过一系列的判断,就会创建一个自动释放池。

所以,第一个问题我们解决了,那就是,在子线程中原本是没有自动释放池的,但是如果有runloop或者autorelease对象的时候,就会自动的创建自动释放池。

那么自动释放池又是什么时候做pop操作的呢?

在autoreleasepool的init方法中我们能看到一些端倪

每个autorelease创建的时候都会监听当前线程的销毁方法,在线程退出时调用tls_dealloc方法。

static void tls_dealloc(void *p) 
    {
        if (p == (void*)EMPTY_POOL_PLACEHOLDER) {
            // No objects or pool pages to clean up here.
            return;
        }

        // reinstate TLS value while we work
        setHotPage((AutoreleasePoolPage *)p);

        if (AutoreleasePoolPage *page = coldPage()) {
            if (!page->empty()) objc_autoreleasePoolPop(page->begin());  // pop all of the pools
            if (slowpath(DebugMissingPools || DebugPoolAllocation)) {
                // pop() killed the pages already
            } else {
                page->kill();  // free all of the pages
            }
        }
        
        // clear TLS value so TLS destruction doesn't loop
        setHotPage(nil);
    }

然后这个tls_dealloc会调用autoreleasePoolPop清楚所有autorelease对象。

你可能感兴趣的:(iOS 子线程的自动释放池)