考虑这个设置:
对象A创建对象B以进行某些工作,并将自身设置为B的委托以获知工作进度。
B对GCD块执行一些操作,并使用委托方法向工作完成发回信号。 A想要在工作完成时拆除(释放)B。
在代码中:
对象A:
B *b = [[B alloc] init];
b.delegate = self;
[b doSomeWork];
- (void) didSomeWorkFromB:(B *)b {
[b release];
b = nil;
}
对象B:
- (void) doSomeWork {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
doSomeWork();
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Work is complete.");
[self.delegate didSomeWorkFromB:self];
});
});
}
问题:在对象A内调用[b release]会导致崩溃。我认为这是因为当A试图释放B时,调度队列/后台代码仍在运行。
问题:在这种情况下,如何正确设置对象和信号,以确保A在所有后台工作完成后仅销毁B?
答案 0 :(得分:1)
虚假问题。它实际上按预期工作,上面的代码不会崩溃。崩溃是由一些不相关的代码引起的。
答案 1 :(得分:0)
你是对的,代码按原样运行。但它不必要地复杂化。
您可以让B&#39 {s} doSomeWork
保留自己(通过在[self retain]
中明确调用[self release]
和doSomeWork
或仅通过引用self
在dispatch_async
块中,它将为我们保留它),并在调用A
后立即清除doSomeWork
,因此didSomeWorkFromB
不需要进一步清理
此模式在iOS中非常常见。例如,如果您查看NSURLConnection
的许多常见实现,因为它在连接下载时保留自身,并在连接完成后自行释放,我们通常不会保留对连接的引用并在connectionDidFinishLoading
清理它。只需让引用计数内存管理的魔力为您处理一切。
在A
:
- (void) test
{
B *b = [[B alloc] init];
b.delegate = self;
[b doSomeWork];
[b release]; // you could also autorelease above, but I just wanted to make it more explicit for the purposes of the demonstration
}
- (void) didSomeWorkFromB:(B *)b
{
// [b release]; // don't need to release it ... we already did
// b = nil; // certainly don't need to nil local reference ... this does nothing useful in any scenario
}
在B
:
- (void) dealloc
{
// let's log this so we can see when it's deallocated
NSLog(@"%s", __FUNCTION__);
[super dealloc];
}
- (void) doSomeWork
{
// [self retain]; // you could manually retain if you want
NSLog(@"%s", __FUNCTION__);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(10);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Work is complete.");
[self.delegate didSomeWorkFromB:self];
// [self release]; // and if you manually retained, you'd manually release, too
});
});
}
在我看来,这种方法(在调用release
之后立即拥有A doSomeWork
B)更加健壮,更紧密地协调对象的平衡清理。我还认为,当你考虑最终转向ARC时,这会让你更好。