都说子线程不主动获取没有runloop,而每个runloop都会在开始创建一个自动释放池,那没有runloop,子线程是否有自动释放池呢?比如下面这个
那么,
- 子线程是否有自动释放池呢
- 这个obj究竟在什么时候释放呢?
- 是在子线程销毁后释放,还是与子线程的生命周期无关?
接下来让我们带着疑问一起去看下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;
}
- cd 到main.m目录
- 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文件,打开此文件,移动到文件底部
接下来假设你已经知道了autoreleasePool的相关知识,不知道的可以看一下这里
首先,在你创建一个autorelease对象的时候,
会调用对象的autorelease方法,这个方法目前在NSObject.h中是没有声明的,不过我们可以看NSObject.mm方法
。。。
最终,会调用到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对象。