我有这样的测试代码
- (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
答案 0 :(得分:19)
没有记录,但在OS X 10.9+和iOS 7 +上答案似乎是是。
Objective-C运行时为open-source,因此您可以阅读源代码以查看正在进行的操作。如果在当前线程上执行没有池的autorelease
,则运行时的最新版本(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
环境变量(默认情况下未设置),当自动释放完成没有池时,它也会设置自动释放堆栈,然后推送一个池(推POOL_SENTINEL
)。
同样地,(当没有查看其他代码时,它很难遵循,但这是相关的部分)当线程被销毁(并且线程局部存储被破坏)时,它会释放所有内容。 autorelease stack(这是pop(0);
所做的)所以它不依赖于用户弹出最后一个池:
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",
(void*)obj, 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
,而不仅仅是NSThread
s。
所以基本上,如果你在OS X 10.9+或iOS 7+上运行,在没有池的线程上自动释放不应该导致泄漏。这没有记录,并且是内部实现细节,因此请谨慎依赖此,因为Apple可能会在未来的操作系统中更改它。但是,我没有看到任何理由为什么他们会删除此功能,因为它很简单,只有好处而且没有缺点,除非他们完全重写自动释放池的工作方式或其他东西。
答案 1 :(得分:1)
Apple documentation says(4 th 段落):
使用通常的alloc和创建
NSAutoreleasePool
对象 初始化消息并用漏极处理它(或释放 - 理解 差异,请参阅垃圾收集)。既然你不能保留一个 自动释放池(或自动释放它 - 见保留和自动释放), 排水池最终会产生解除分配的效果。您 应始终在相同的上下文中排出自动释放池 (调用方法或函数,或循环体)它是什么 创建。有关详细信息,请参阅使用自动释放池块。