使用 GCD 并回调主线程时如何设置正确的对象拆卸?
考虑以下设置:
对象 A 创建对象 B 来执行某些工作,并将其自身设置为 B 的委托以了解工作进度。
B 使用 GCD 块进行一些工作,并使用委托方法向 A 发出有关工作完成的信号。 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已完成?
Consider this setup:
Object A creates object B for doing some work, and sets itself as B's delegate to be informed of work progress.
B does some work with GCD blocks, and signals back to A with the delegate method about work completion. A wants to tear down (release) B upon work completion.
In code terms:
Object A:
B *b = [[B alloc] init];
b.delegate = self;
[b doSomeWork];
- (void) didSomeWorkFromB:(B *)b {
[b release];
b = nil;
}
Object 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];
});
});
}
PROBLEM: calling [b release] inside object A causes a crash. I think it's because the dispatch queue/background code is still running when A tries to release B.
QUESTION: how do I properly set up the objects and signaling in this case, to make sure that A only destroys B when all the background work has been completed?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
虚假的问题。它实际上按预期工作,并且上面的代码不会崩溃。崩溃是由一些不相关的代码引起的。
Bogus question. It actually works as expected and the code above does not crash. The crash was caused by some unrelated code.
你是对的,代码按原样工作。但这是不必要的复杂。
您可以让 B 的
doSomeWork
保留自身(通过在doSomeWork[self keep]
和[self release]
code> 或仅在dispatch_async
块中引用self
,这将为我们保留它),并让A
在调用后立即清理doSomeWork
,因此在didSomeWorkFromB
中不需要进一步清理。这种模式在 iOS 中很常见。例如,如果您查看
NSURLConnection
的许多常见实现,因为它在下载连接时保留自身,并在连接完成后释放自身,所以我们通常不会同时保留对连接并在connectionDidFinishLoading
中清理它。只需让引用计数内存管理的魔力为您处理一切即可。在
A
中:在
B
中:在我看来,这种方法(在调用
doSomeWork
后立即让 Arelease
B )更稳健,更紧密地协调对象的平衡清理。我还认为,当您考虑最终转向 ARC 时,这会让您处于更好的位置。You are correct, that the code works as is. But it is unnecessarily complicated.
You can just have B's
doSomeWork
retain itself (either by explicitly calling[self retain]
and[self release]
indoSomeWork
or just by referencingself
in thedispatch_async
block, which will retain it for us), and letA
clean up immediately after the invocation ofdoSomeWork
, and therefore no further cleanup is required indidSomeWorkFromB
.This pattern is very common in iOS. For example, if you look at many common implementations of
NSURLConnection
, since it retains itself while the connection is downloading, and releases itself once the connection is done, we often don't both keeping a reference to the connection and cleaning it up inconnectionDidFinishLoading
. Just let the magic of reference counting memory management take care of everything for you.In
A
:In
B
:In my opinion, this approach (of having A
release
B immediately after callingdoSomeWork
) is more robust, more closely coordinating the balancing cleanup of the object. I also think this puts you in better stead as you contemplate an eventual shift to ARC.