基于Bavarious's answer to this SO question,如果您使用LLVM / clang构建,我会相信@autoreleasepool
现在是Objective-C语言功能。
在这种情况下,如何在非ARC环境中重写以下代码以使用@autoreleasepool
而不是NSAutoreleasePool
?
[NSAutoreleasePool addObject:anObject]; // (*)
背景:我基本上想编写一个-autorelease
的自定义实现,它不会以任何方式 与NSAutoreleasePool
类进行交互:
@autoreleasepool {
SomeCls *obj = [[SomeCls alloc] init];
[obj autorelease]; // Does not go through an NSAutoreleasePool object
// ...
}
答案 0 :(得分:3)
@autoreleasepool { }
是一种新的语言功能,旨在消除在函数的每个出口点消耗当前池的需要。现在,而不是写:
void f(void) {
//Make a new pool
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
//Every inner scope that exits prematurely requires a drain
if (life_is_good) {
for (int i = 0; i < 1000; i++) {
NSObject *obj = [[[NSObject alloc]init]autorelease];
//breaks out of the loop, and the function, so drain the pool
if (life_is_bad) {
[pool drain];
return;
}
}
//Life gets bad below here, so return
[pool drain];
return;
}
//end of function requires drain.
[pool drain];
}
您可以将整个函数包装在@autoreleasepool { }
指令中,编译器将插入函数返回的正确排水:
void f(void) {
//Make a new pool
@autoreleasepool {
if (life_is_good) {
for (int i = 0; i < 1000; i++) {
NSObject *obj = [[[NSObject alloc]init]autorelease];
//breaks out of the loop, and the function, so drain the pool
if (life_is_bad) {
//[pool drain]; auto-drain
return;
}
}
//[pool drain]; auto-drain
return;
}
//[pool drain]; auto-drain
}
}
我不知道您为什么要放弃自动池机制,因为它为您处理巨大的工作量(页面管理,每线程池管理,及时释放对象)但是有效的方式),但是让原始的NSAutoreleasePool运行起来并不是一件容易的麻烦。任何自动释放池都需要页面管理器(malloc()
和realloc()
),指针存储(一个简单的数组),以及某种方式来检测当前使用它的范围是否已经退出({ {1}})。一个简单的池可能只是一个线程安全的单例,但要注意你尝试使用该实现一次插入和删除多少个对象。如果您尝试为太多对象分配太多热门页面,则可能会出现段错误。
答案 1 :(得分:1)
基于Bavarious对这个SO问题的回答,我认为如果你用LLVM / clang构建,@ autoreleasepool现在是一个Objective-C语言功能。
正确。
在这种情况下,如何在非ARC环境中重写以下代码以使用@autoreleasepool而不是NSAutoreleasePool?
[NSAutoreleasePool addObject:anObject]; //(*)
文档: 通常您不直接调用此方法 - 而是将对象自动发送到对象。
@autoreleasepool {
id anObject = ...;
[anObject autorelease];
}
背景:我基本上想编写一个自定义的-autorelease实现,它不会以任何方式与NSAutoreleasePool类进行交互:
你甚至不能再继承NSAutoreleasePool
(以有意义的方式)。除非您永远不会将该对象传递给其他可自由释放的API,否则您的对象将最终出现在当前的自动释放池中。今天的机制甚至不是基于对象的。
如果这是不可能的,我会接受不涉及NSAutoreleasePool的解决方案建议。
不确定您要解决的问题,但您可以使用以下方式延长生命周期:
NSMutableArray * fauxAutoreleasePool = NSMutableArray.new;
id anObject = ...;
[fauxAutoreleasePool addObject:anObject];
...
[fauxAutoreleasePool removeAllObjects]; // << explicitly drain, which also happens when the array is destroyed
当然,您可以编写自己的实现。你为什么要编写自己的东西仍然是个谜,对于生产代码不是一个好主意(@autoreleasepool
效果很好)。
答案 2 :(得分:0)
在@autoreleasepool
块的末尾,自动释放的对象会发送release
消息。
如果您想重新实现autorelease
方法,则可能不希望直接向release
子类发送NSObject
消息。如果是这样,重新实施release
以及某些特殊行为可以帮助您开展业务。
答案 3 :(得分:0)
背景:我基本上想写一个自定义的实现 -autorelease不与NSAutoreleasePool类交互:
是什么让您认为-autorelease
与NSAutoreleasePool
互动?
只要您在iOS 5+或Mac OS X 10.7+上运行,@autoreleasepool
和-[NSObject autorelease]
都将使用运行时自动释放池管理功能,并且与{{无关} 1}}所有(和那些OS版本上的NSAutoreleasePool
只是运行时函数的包装器。)
答案 4 :(得分:0)
虽然我已经将答案标记为已接受并且当时提供了赏金,但我已经找到了我正在寻找的真正答案,并意识到我的问题不是很明确 - 主要是因为我不知道是什么问。
如何与@autoreleasepool互动:
当编译器遇到@autoreleasepool { }
块时,它会自动插入两个函数调用。在块的开头,它插入C函数调用:
void *_objc_autoreleasePoolPush(void);
在@autoreleasepool
块结束时(包括遇到return
或break
时 - 但不是根据LLVM docs抛出异常时)编译器插入C函数调用:
_objc_autoreleasePoolPop(void *ctxt); // ctxt is Apple's label for the param
我相信void *ctxt
参数是当前@autoreleasepool
开始位置的指标(实际上仅在Apple的实施中有用 - 请参阅here)。无论如何,我发现在自定义实现中很容易忽略它。
-autorelease
选择器:
基本上,方法是:
_objc_autoreleasePoolPush()
中创建(如果是最外面的块)或标记(如果是内部块)自动释放池对象(在C或C ++中)。对我来说,使用Stack数据结构似乎最容易,但YMMV。-autorelease
选择器的范围内,或者您具有访问它的故障安全方式。-autorelease
内的自动释放池对象(以任何可能的方式)。_objc_autoreleasePoolPop(void *)
中,将-release
消息发送到自动释放池对象中的每个项目,直到达到_objc_autoreleasePoolPush()
中设置的标记(或者到达池底部)。然后进行任何额外的必要清理。有关线程的最终说明:
Objective-C @autoreleasepools
应该是线程本地的。也就是说,每个线程都有自己的(如果它需要一个)。因此,_objc_autoreleasePoolPush()
必须有某种方法来确定当前线程是否已经有一个活动的自动释放池,如果没有,则创建该对象。
类似地,这就是为什么在创建线程时,线程必须首先打开一个@autoreleasepool { }
块然后才能执行任何其他操作(在Objective-C中),并且它必须做的最后一件事就是关闭@autoreleasepool
块。 1}}阻止(隐式地通过break
或return
或使用明确的直通)。
最后三次观察:
@autoreleasepool { }
中的main()
块开头。NSThread
课程和GCD
而非DIY线程的原因 - 他们会为您处理所有这些事情!