使用 NSOperation 子类 (ARC) 对主线程的块回调
这个问题类似于这个问题,具有自动引用 我有一个 NSOperation 子类,它接受一个块参数,
该参数旨在作为主 (UI) 线程的回调。我的初衷是在后台执行一些操作,然后使用dispatch_async和主队列来执行回调。
最初的前提:
@interface MySubclass : NSOperation {
@protected
dispatch_block_t _callback;
}
- (id)initWithCallback:(dispatch_block_t)callback;
@end
@implementation MySubclass
- (void)main
{
// Do stuff
if (![self isCancelled]) {
dispatch_async(dispatch_get_main_queue(), _callback);
}
}
@end
当块范围之外对 UIKit 对象的所有引用都被删除时,就会出现问题。 (例如,UIViewController
从导航堆栈中弹出。)这会留下对块内对象的唯一引用,因此当块位于该块所在的线程上时,该对象将被释放。被释放。。从主线程中释放 UIKit 对象会导致应用程序崩溃,并显示错误消息 尝试从主线程或 Web 线程以外的线程获取 Web 锁。这可能是从辅助线程调用 UIKit 的结果。现在崩溃了...
作为一种解决方法,我向回调 ivar 添加了一个 __block
修饰符,并使用 dispatch_sync
来确保释放的所有内容都在 main 上线。
@interface MySubclass : NSOperation {
@protected
__block dispatch_block_t _callback;
}
- (id)initWithCallback:(dispatch_block_t)callback;
@end
@implementation MySubclass
- (void)main
{
// Do Stuff
if (![self isCancelled]) {
dispatch_block_t block = ^{
_callback();
_callback = nil;
};
// Cover all our bases to prevent deadlock
if ([NSThread isMainThread]) block();
else dispatch_sync(dispatch_get_main_queue(), block);
}
}
@end
我想知道是否有更好的方法可以在这个前提下完成某些事情。我的解决方法感觉很hacky,而且我不喜欢这样,我可能最终会在队列中出现多个操作,所有这些操作都在等待主线程的启动才能完成。
This question is similar to this question with automatic reference counting thrown in.
I have an NSOperation
subclass that accepts a block argument that is intended as a callback to the main (UI) thread. My original intention was to perform some operation in the background, and then use dispatch_async
and the main queue to perform the callback.
Original premise:
@interface MySubclass : NSOperation {
@protected
dispatch_block_t _callback;
}
- (id)initWithCallback:(dispatch_block_t)callback;
@end
@implementation MySubclass
- (void)main
{
// Do stuff
if (![self isCancelled]) {
dispatch_async(dispatch_get_main_queue(), _callback);
}
}
@end
Problems arise when all references to a UIKit object outside the scope of the block are removed. (E.g. a UIViewController
is popped off a navigation stack.) This leaves the only reference to the object inside the block, so the object is deallocated when the block is, on the thread where the block is deallocated. Deallocating a UIKit object off the main thread crashes the app with the error message Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
As a workaround, I added a __block
modifier to the callback ivar, and am using dispatch_sync
to make sure everything released is on the main thread.
@interface MySubclass : NSOperation {
@protected
__block dispatch_block_t _callback;
}
- (id)initWithCallback:(dispatch_block_t)callback;
@end
@implementation MySubclass
- (void)main
{
// Do Stuff
if (![self isCancelled]) {
dispatch_block_t block = ^{
_callback();
_callback = nil;
};
// Cover all our bases to prevent deadlock
if ([NSThread isMainThread]) block();
else dispatch_sync(dispatch_get_main_queue(), block);
}
}
@end
I am wondering if there is a better way to accomplish something with this premise. My workaround feels hacky, and I don't like that I might end up with several operations in my queue all waiting for a turn on the main thread before they can complete.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您需要确保回调运行,即使控制器已从堆栈中弹出,那么您的解决方法是正确的。
但是,如果您确实只需要在控制器仍然存在的情况下运行回调,那么在回调中使用弱引用来确保块本身不会首先保留控制器会更简单。它看起来像这样:
If you need to ensure the callback runs even if the controller has been popped from the stack, then your workaround is correct.
If, however, you really only need the callback to run if the controller is still around, then it would be simpler to use weak references in the callback to ensure that the block itself doesn't retain the controller in the first place. It would look something like this:
API 的用户应该维护对 UIView 和任何其他存在此问题的对象的弱引用。然后回调将不再保留 UIView。在块内,他们应该将弱引用分配给强引用,测试该强引用是否为零,然后适当地进行。
视图控制器应该小心,不要不必要地实例化它们的视图。在访问
[self view]
之前,请始终使用[self isViewLoaded]
。 (这也适用于UITableView
子类中的[self tableView]
,因为这只是view
的正确键入的别名。)Users of your API should maintain weak references to UIViews and any other objects with this problem. The callback will then no longer keep the UIView around. Within the block, they should assign the weak reference to a strong reference, test that strong reference against nil, and proceed appropriately.
View controllers should be careful not to unnecessarily instantiate their views. Always use
[self isViewLoaded]
prior to accessing[self view]
. (This also applies to[self tableView]
inUITableView
subclasses, since that's just a correctly typed alias forview
.)