【问题标题】:How to update UI in a task completion block?如何在任务完成块中更新 UI?
【发布时间】:2011-08-26 13:42:21
【问题描述】:

在我的应用程序中,我让进度指示器在发送 HTTP 请求之前启动动画。 完成处理程序在块中定义。获得响应数据后,我将进度指示器隐藏在块内。我的问题是,据我所知,UI 更新必须在主线程中执行。如何确定?

如果我在窗口控制器中定义一个更新UI的方法,并让块调用该方法而不是直接更新UI,这是一个解决方案吗?

【问题讨论】:

    标签: cocoa-touch cocoa objective-c-blocks


    【解决方案1】:

    此外,如果您的应用面向 iOS >= 4,您可以使用 Grand Central Dispatch:

    dispatch_async(dispatch_get_main_queue(), ^{
        // This block will be executed asynchronously on the main thread.
    });
    

    当您的自定义逻辑无法用 performSelect… 方法采用的单个选择器和对象参数轻松表达时,这很有用。

    要同步执行块,请使用dispatch_sync() - 但请确保您当前不在主队列上执行,否则 GCD 将死锁。

    __block NSInteger alertResult; // The __block modifier makes alertResult writable
                                   // from a referencing block.
    void (^ getResponse)() = ^{
        NSAlert *alert = …;
        alertResult = [NSAlert runModal];
    };
    
    if ([NSThread isMainThread]) {
        // We're currently executing on the main thread.
        // We can execute the block directly.
        getResponse();
    } else {
        dispatch_sync(dispatch_get_main_queue(), getResponse);
    }
    
    // Check the user response.
    if (alertResult == …) {
        …
    }
    

    【讨论】:

    • 我可以嵌入这个 dispatch_async(dispatch_get_main_queue(), ^{});在另一个街区内?就我而言,我需要在主线程上同步执行该块。我会显示一个 NSAlert 并要求用户做出回应。
    • 当然,您可以随意嵌套块。唯一需要注意的是,您不能将 同步 执行块提交到当前队列 i。 e. dispatch_sync() 到您的代码当前正在执行的队列中(GCD 会死锁)。但这不适用于使用dispatch_async()asynchronous 方法,如上面的答案示例中所示。如果您需要dispatch_sync(dispatch_get_main_queue(), …),请确保dispatch_get_current_queue() 不是主队列。 [NSThread isMainThread] 将在您的示例中起到相同的作用。
    • 谢谢。现在我对块和主线程的理解更加清晰了。
    【解决方案2】:

    你可能误会了什么。使用块并不意味着您的代码在后台线程中运行。有许多插件异步工作(在另一个线程中)并使用块。

    有几个选项可以解决您的问题。

    您可以使用[NSThread isMainThread] 检查您的代码是否在主线程中运行。这可以帮助您确保您不在后台。

    您还可以使用performSelectorInMainThread:SELperformSelectorInBackground:SEL 在主或后台执行操作。

    当您尝试从 bakcground 线程调用 UI 时,应用程序会立即崩溃,因此很容易找到错误。

    【讨论】:

    • 谢谢。我在块内运行 [NSThread isMainThread],它返回 NO。这是一个 Mac 应用程序,它不会崩溃。在方法 - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait 中,arg 的类型是 id。如何通过 BOOL 来使用这个方法?
    • 在块内更新UI时应用不会崩溃?试试 [NSNumber numberWithBOOL:YES]
    • 我再次运行,[NSThread isMainThread]返回0,是NO,对吧?在块中,我隐藏了一个进度指示器,我认为它是一个 UI 更新。并且应用程序不会崩溃。
    • 是的,实际上不是。无论如何尝试 if([NSThread isMainThread]) { NSLog(@"ok"); }。日志里有ok吗?
    • 我的代码是 if ([NSThread isMainThread]) {NSLog(@"Main Thread");} else {NSLog(@"NO");}。它打印“否”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-25
    • 2013-10-08
    相关资源
    最近更新 更多