2

我正在寻找一种通用且优雅的方式来管理界面更新。我知道用户界面代码必须在主线程中运行,所以当我需要一些计算或网络任务时,我使用 GDC 和这种模式:

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(aQueue, ^() {

//Backgroud code

    dispatch_sync(dispatch_get_main_queue(), ^{
    //Update the UI
    }
}

这段代码的问题是我需要始终检查用户在我的计算过程中是否改变了视图,所以代码如下:

dispatch_sync(dispatch_get_main_queue(), ^{
    if (mylabel != nil)  && ([mylabel superview] != nil) {
        mylabel.text = _result_from_computation_;
    }
}

有一些最好的方法吗?

谢谢。

4

2 回答 2

1

你很好。但是,如果您想阅读更多内容或想要更彻底地解释正在发生的事情......

您应该阅读 Apple Docs Grand Central Dispatch (GCD) Reference并观看 WWDC 2012 视频Session 712 - Asynchronous Design Patterns with Blocks, GCD and XPC

如果您使用的是 iOS,则可以忽略 XPC(进程间通信),因为当前操作系统版本(撰写本文时为 6.1)不支持它。

示例:在背景中加载大图像并在完成时设置图像。

@interface MyClass ()
@property (strong) dispatch_block_t task;
@end

@implementation MyClass
- (void)viewDidLoad {
    self.task = ^{
        // Background Thread, i.e., your task
        NSImage *image = [[NSImage alloc] initWithData:data];
        dispatch_async(dispatch_get_main_queue(), ^{
            // Main Thread, setting the loaded image
            [view setImage:image];
        });
    });
}

- (IBAction)cancelTaskButtonClick:(id)sender {  // This can be -viewWillDisappear
    self.task = nil;  // Cancels this enqueued item in default global queue
}

- (IBAction)runTaskButtonClick:(id)sender {
    // Main Thread
    dispatch_queue_t queue;
    queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, self.task);
}

为了稍后取消并重新加载界面,您所要做的就是将 dispatch_block_t 变量设置为 nil。

也许更具体地解决您的问题,此示例代码处理从 Descriptor 读取数据,即磁盘或网络。

通常,您将使用 Call-Callback 模式,该模式本质上是获取后台线程,执行任务,完成后调用另一个块以获取主线程来更新 UI。

希望这可以帮助!

于 2013-02-01T09:21:55.420 回答
0

您可以检查视图窗口属性:

if (myLabel.window) {
  // update label
}

这是多余if (label != nil)的,因为如果 label 为 nil,那么所有 label 属性也将为 nil(或零)并且设置它们不会引发异常。

于 2013-02-01T07:55:25.230 回答