Block_release在后台线程上释放UI对象

时间:2011-06-15 05:34:44

标签: ios grand-central-dispatch objective-c-blocks

WWDC 2010“Blocks and Grand Central Dispatch”演讲中提出的模式之一是使用嵌套的dispatch_async调用在后台线程上执行耗时的任务,然后在任务完成后更新主线程上的UI < / p>

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”正在块中使用,它会自动获得“保留”,并在清除块时稍后获得“释放”。

如果块的'release'调用是最终的释放调用(例如,用户在后台任务运行时导航远离视图),则会调用myViewController dealloc方法 - 但它会在后台线程上调用!!

UIKit对象不喜欢在主线程之外取消分配。就我而言,UIWebView会抛出异常。

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

3 个答案:

答案 0 :(得分:11)

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

__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,__block变量对象由块自动保留,但我们可以将_llock值设置为__block变量,以便随时释放保留的对象。

__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
    }
});

答案 1 :(得分:2)

在一个帖子中,我只是在线程使用结束时使用[viewController retain]; [viewController release]。它有效,我不使用GCD~

答案 2 :(得分:-2)

这对我有用(添加了一个计时器):

[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
}