4

这个问题是类似于this question与自动参考计数投入。从的NSOperation子类(ARC)使用块回调主线程

我有一个NSOperation子类,接受旨在作为回调一个块参数到主(UI)线程。我的初衷是在后台执行一些操作,然后使用dispatch_async和主队列执行回调。

原始前提:当被删除到块的范围之外的UIKit的对象的所有引用出现

@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 

问题。 (例如,从导航堆栈弹出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...

变通方法是应用程序,我加了__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 

我想知道是否有更好的方法来完成这个前提。我的解决方法让人感觉很不舒服,而且我不喜欢这样,我可能会在我的队列中完成几项操作,并在完成之前等待主线程启动。

+0

另一方面,我遇到了在代码结束时检查的类型的死锁,所以现在使用主线程上的dispatch_sync助手函数:http://stackoverflow.com/questions/5225130/grand-central-dispatch-gcd-vs-performselector-need-a-better-explanation/5226271#5226271 –

回答

4

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

但是,如果你真的只需要回调就可以运行,如果控制器还在,那么在回调中使用弱引用将会更简单,以确保块本身不会保留第一个控制器地点。这将是这个样子:

- (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 
} 
+1

我不一定需要回调才能运行,但我试图更新现有的项目,仍然需要支持iOS 4.x.不幸的是,在4.x中,你必须使用'__unsafe_unretained'而不是'__weak'。任何人有更多的想法?感谢迄今为止的反馈! – sho

1

您的API的用户应保持对UIViews和弱引用这个问题的任何其他对象。回调将不再保持UIView。在该块内,他们应该将弱引用分配给强引用,测试该引用强对齐,并适当地执行。

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