- (void)viewDidLoad { [super viewDidLoad]; NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil]; [thread start]; } -(void)test { MyClass *my = [[[MyClass alloc] init] autorelease]; NSLog(@"%@",[my description]); }
我没有为自己的线程创建任何自动释放池,但是当线程退出时,对象“我的”只是dealloc.why?
即使我改变我的测试代码如下
- (void)viewDidLoad { [super viewDidLoad]; NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil]; [thread start]; } -(void)test { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; MyClass *my = [[[MyClass alloc] init] autorelease]; NSLog(@"%@",[my description]); }
我创建了自己的autoreleasepool但在线程退出时不会将其耗尽.对象“我的”仍然可以dealloc.为什么?
我使用Xcode5而不使用ARC
解决方法
Objective-C运行时为open-source,因此您可以阅读源代码以查看正在进行的操作.如果在当前线程上执行没有池的自动释放,则运行时的最新版本(646,OS X 10.10和iOS 8附带)确实会添加池.在NSObject.mm:
static __attribute__((noinline)) id *autoreleaseNoPage(id obj) { // No pool in place. assert(!hotPage()); if (obj != POOL_SENTINEL && DebugMissingPools) { // We are pushing an object with no pool in place,// and no-pool debugging was requested by environment. _objc_inform("MISSING POOLS: Object %p of class %s " "autoreleased with no pool in place - " "just leaking - break on " "objc_autoreleaseNoPool() to debug",(void*)obj,object_getClassName(obj)); objc_autoreleaseNoPool(obj); return nil; } // Install the first page. AutoreleasePoolPage *page = new AutoreleasePoolPage(nil); setHotPage(page); // Push an autorelease pool boundary if it wasn't already requested. if (obj != POOL_SENTINEL) { page->add(POOL_SENTINEL); } // Push the requested object. return page->add(obj); }
当您推送第一个池时(在这种情况下推送的东西是POOL_SENTINEL),或者您在没有池的情况下自动释放,就会调用此函数.当推送第一个池时,它会设置自动释放堆栈.但是从代码中可以看出,只要未设置DebugMissingPools环境变量(默认情况下不设置),当autorelease完成没有池时,它也会设置自动释放堆栈,然后推送池(推送一个池) POOL_SENTINEL).
类似地,(在没有查看其他代码的情况下有点难以理解,但这是相关部分)当线程被销毁(并且线程局部存储被销毁)时,它会释放自动释放堆栈中的所有内容(这就是pop(0); does)所以它不依赖于用户弹出最后一个池:
static void tls_dealloc(void *p) { // reinstate TLS value while we work setHotPage((AutoreleasePoolPage *)p); pop(0); setHotPage(nil); }
之前版本的运行时(551.1,OS X 10.9和iOS 7附带)也是这样做的,你可以从NSObject.mm看到:
static __attribute__((noinline)) id *autoreleaseSlow(id obj) { AutoreleasePoolPage *page; page = hotPage(); // The code below assumes some cases are handled by autoreleaseFast() assert(!page || page->full()); if (!page) { // No pool. Silently push one. assert(obj != POOL_SENTINEL); if (DebugMissingPools) { _objc_inform("MISSING POOLS: Object %p of class %s " "autoreleased with no pool in place - " "just leaking - break on " "objc_autoreleaseNoPool() to debug",object_getClassName(obj)); objc_autoreleaseNoPool(obj); return nil; } push(); page = hotPage(); } do { if (page->child) page = page->child; else page = new AutoreleasePoolPage(page); } while (page->full()); setHotPage(page); return page->add(obj); }
但之前的版本(532.2,OS X 10.8和iOS 6附带),does not:
static __attribute__((noinline)) id *autoreleaseSlow(id obj) { AutoreleasePoolPage *page; page = hotPage(); // The code below assumes some cases are handled by autoreleaseFast() assert(!page || page->full()); if (!page) { assert(obj != POOL_SENTINEL); _objc_inform("Object %p of class %s autoreleased " "with no pool in place - just leaking - " "break on objc_autoreleaseNoPool() to debug",obj,object_getClassName(obj)); objc_autoreleaseNoPool(obj); return NULL; } do { if (page->child) page = page->child; else page = new AutoreleasePoolPage(page); } while (page->full()); setHotPage(page); return page->add(obj); }
请注意,上述内容适用于任何pthread,而不仅仅适用于NSThreads.
所以基本上,如果你在OS X 10.9或iOS 7上运行,在没有池的线程上自动释放不应该导致泄漏.这没有记录,并且是内部实现细节,因此请谨慎依赖此,因为Apple可能会在未来的操作系统中对其进行更改.但是,我没有看到任何理由为什么他们会删除此功能,因为它很简单,只有好处而且没有缺点,除非他们完全重写自动释放池的工作方式或其他方式.