如您所知,在ARC中,块中使用的__block
对象指针类型变量由块保留。因此,请采用以下简化示例:
__block id foo = getObject();
void (^aBlock)() = ^ {
NSLog(@"%@", foo);
foo = getObject();
}
runBlockAsynchronouslyMultipleTimes(aBlock);
foo
指向的对象由块保留,因此当块(异步)运行时,该对象仍然有效并且可以打印。当我们在块中进行赋值时,ARC会像任何其他强引用一样管理它(旧值被释放并保留新值)。 (赋值强制我们首先使用__block
。)当不再需要块时,ARC会以某种方式释放foo
指向的保留对象(它不会被泄露)
好的,现在假设我想在MRC下做同样的事情(为什么不重要;这是关于语言的问题)。如您所知,块中使用的对象指针类型的__block
变量不会被MRC中的块保留。哪个好;我们自己管理它(毕竟这是MRC)。所以尝试看起来像这样:
__block id foo = [getObject() retain];
void (^aBlock)() = ^ {
NSLog(@"%@", foo);
[foo release];
foo = [getObject() retain];
}
runBlockAsynchronouslyMultipleTimes(aBlock);
// where to release foo?
大多数是直截了当的 - 我们最初手动保留了对象;在块内部,当我们重新分配指针时,我们会根据需要释放并保留新值。
但问题就出现了:当不再需要块时,我们如何释放对象?由于我们手动管理内存,理想情况下,当释放块时,我们应该手动释放对象。但似乎没有一种简单的方法可以做到这一点。
我可能想到一种方法:使用关联引用将对象绑定到块。但是然后要在块内重新分配关联引用,块需要对自身的引用,因此块变量也需要__block
,并且需要在设置变量之前复制块。这一切都非常难看。或者,我们将对象放在一个可变容器对象中,然后由块保留;但那也很难看。
答案 0 :(得分:0)
可变容器尽可能干净。您可以使用单个object
属性创建一个简单的包装器来清理它,然后从属性访问器中获取内存管理。
一种看起来更干净,但实际上有点混乱的方法,就是拥有一个带有指针的不可变包装器,并在解除分配时释放该指针。
@interface ObjectReleaser : NSObject {
id *objectPointer;
}
- (id)setObjectPointer:(id *)pointer;
- (void)captureMe;
@end
@implementation ObjectReleaser
- (void)setObjectPointer:(id *)pointer {
if(!objectPointer && pointer) {
objectPointer = pointer;
[*objectPointer retain];
}
}
- (void)dealloc {
if(objectPointer) [*objectPointer release];
[super dealloc];
}
- (void)captureMe {} // Blocks can call this to capture the object
@end
该块将捕获并保留此对象,因为它不是__block
。您可以像往常一样使用所有正确的__block
和retain
来修改release
个对象。然后,当块被释放时,它将释放释放器,然后释放器将被解除分配并释放指针当前指向的任何内容。
__block id foo = getObject();
ObjectReleaser *releaser = [[ObjectReleaser alloc] init];
void (^aBlock)() = ^ {
[releaser captureMe];
NSLog(@"%@", foo);
[foo release];
foo = [getObject() retain];
}
aBlock = [aBlock copy];
[releaser setObjectPointer:&foo];
请注意,您不需要仅为块保留foo
,因为发布者会为您执行此操作。 do 必须在复制块之后设置发布者的指针,因为副本将更改foo
的指针。这也是为什么在函数返回后保存堆栈变量的指针是安全的:变量实际上不在堆栈上。