【问题标题】:Using block callbacks to the main thread from an NSOperation subclass (ARC)使用从 NSOperation 子类 (ARC) 到主线程的块回调
【发布时间】:2011-10-20 15:33:42
【问题描述】:

这个问题类似于this question 引入了自动引用计数。

我有一个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 对象会使应用程序崩溃,并显示错误消息 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...

作为一种解决方法,我在回调 ivar 中添加了一个 __block 修饰符,并使用 dispatch_sync 确保所有发布的内容都在主线程上。

@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

我想知道在这个前提下是否有更好的方法来完成某件事。我的解决方法感觉很老套,而且我不喜欢这样,我的队列中可能会有几个操作都在等待主线程打开,然后才能完成。

【问题讨论】:

标签: objective-c ios grand-central-dispatch automatic-ref-counting


【解决方案1】:

如果即使控制器已从堆栈中弹出,您也需要确保回调运行,那么您的解决方法是正确的。

然而,如果你真的只需要在控制器还在的情况下运行回调,那么在回调中使用弱引用来确保块本身不会首先保留控制器会更简单.它看起来像这样:

- (void)demoMethod {
    __weak id weakSelf = self;
    MySubclass *subclass = [[MySubclass alloc] initWithCallback:^{
        if (!weakSelf) {
            return;
        }
        else {
            // Do whatever the callback does here
        }
    }];

    // Do something with `subclass` here
}

【讨论】:

  • 我不一定需要回调来运行,但我正在尝试更新现有项目并且仍然需要支持 iOS 4.x。不幸的是,在 4.x 上,您必须使用 __unsafe_unretained 而不是 __weak。有人有更多的想法吗?感谢您迄今为止的反馈!
【解决方案2】:

您的 API 用户应维护对 UIView 和任何其他存在此问题的对象的弱引用。回调将不再保留 UIView。在块内,他们应该将弱引用分配给强引用,针对 nil 测试该强引用,然后进行适当的处​​理。

视图控制器应该注意不要不必要地实例化他们的视图。在访问 [self view] 之前,请始终使用 [self isViewLoaded]。 (这也适用于UITableView 子类中的[self tableView],因为这只是view 的正确键入的别名。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-03
    相关资源
    最近更新 更多