Block_release在后台线程上取消分配UI对象

在WWDC 2010“块和大中央调度”演讲中提出的模式之一是使用嵌套的dispatch_async调用在后台线程上执行耗时的任务,然后在任务完成后更新主线程上的UI

dispatch_async(backgroundQueue, ^{ // do something time consuming in background NSArray *results = ComputeBigKnarlyThingThatWouldBlockForAWhile(); // use results on the main thread dispatch_async(dispatch_get_main_queue(), ^{ [myViewController UpdateUiWithResults:results]; }); }); 

由于在块内部使用了“myViewController”,它会自动得到一个“保留”,并在清理块之后得到一个“释放”。

如果块的“释放”调用是最终的释放调用(例如,用户在后台任务运行时离开视图),则调用myViewController dealloc方法 – 但是在后台线程调用它!

UIKit对象不喜欢在主线程之外解除分配。 在我的情况下,UIWebView引发一个exception。

这个WWDC如何呈现模式 – 特别提到是避免UIlocking的最佳新方法 – 是如此的有缺陷? 我错过了什么吗?

对于这种情况,您可以使用__block存储types限定符。 __blockvariables不会被块自动保留。 所以你需要自己保留这个对象:

 __block UIViewController *viewController = [myViewController retain]; dispatch_async(backgroundQueue, ^{ // Do long-running work here. dispatch_async(dispatch_get_main_queue(), ^{ [viewController updateUIWithResults:results]; [viewController release]; // Ensure it's released on main thread } }); 

编辑

使用ARC,__blockvariables对象被块自动保留,但是我们可以设置nil值给__blockvariables来释放被保留的对象。

 __block UIViewController *viewController = myViewController; dispatch_async(backgroundQueue, ^{ // Do long-running work here. dispatch_async(dispatch_get_main_queue(), ^{ [viewController updateUIWithResults:results]; viewController = nil; // Ensure it's released on main thread } }); 

在一个线程中,我只使用[viewController retain]; 那么在线程的末尾使用[viewController release] 。 它工作,我不使用GCD~

这对我工作(添加一个计时器):

 [self retain]; // this guarantees that the last release will be on the main threaad dispatch_async(backgroundQueue, ^{ // do something time consuming in background NSArray *results = ComputeBigKnarlyThingThatWouldBlockForAWhile(); // use results on the main thread dispatch_async(dispatch_get_main_queue(), ^{ [myViewController UpdateUiWithResults:results]; [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(releaseMe:) userInfo:nil repeats:NO]; }); }); - (void)releaseMe:(NSTimer *)theTimer { [self release]; // will be on the main thread }