GCD、线程、程序流和 UI 更新 [英] GCD, Threads, Program Flow and UI Updating

查看:26
本文介绍了GCD、线程、程序流和 UI 更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很难弄清楚如何把这一切放在一起.我在 Mac 上有一个解谜应用程序.你进入拼图,按下一个按钮,当它试图找到解决方案的数量时,最小移动等我想保持用户界面更新.计算完成后,重新启用按钮并更改标题.

I'm having a hard time figuring out how to put this all together. I have a puzzle solving app on the mac. You enter the puzzle, press a button, and while it's trying to find the number of solutions, min moves and such I would like to keep the UI updated. Then once it's finished calculating, re-enable the button and change the title.

下面是按钮选择器的一些示例代码,以及求解函数:(请记住,我是从 Xcode 复制/粘贴的,因此可能缺少一些 {} 或其他一些错别字......但它应该让你知道我想要做什么.

Below is some sample code from the button selector, and the solving function: ( Please keep in mind I copy/paste from Xcode so there might be some missing {} or some other typos.. but it should give you an idea what I'm trying to do.

基本上,用户按下一个按钮,该按钮是 ENABLED=NO,调用函数来计算拼图.在计算时,使用移动/解决方案数据更新 UI 标签.然后一旦完成计算拼图,按钮是 ENABLED=YES;

Basicly, user presses a button, that button is ENABLED=NO, Function called to calculate puzzle. While it's calculating, keep the UI Labels updated with moves/solution data. Then once it's finished calculating the puzzle, Button is ENABLED=YES;

按下按钮时调用:

- (void) solvePuzzle:(id)sender{
    solveButton.enabled = NO;
    solveButton.title = @"Working . . . .";

    // I've tried using this as a Background thread, but I can't get the code to waitTilDone before continuing and changing the button state.
    [self performSelectorInBackground:@selector(createTreeFromNode:) withObject:rootNode];

    // I've tried to use GCD but similar issue and can't get UI updated.
    //dispatch_queue_t queue = dispatch_queue_create("com.gamesbychris.createTree", 0);
    //dispatch_sync(queue, ^{[self createTreeFromNode:rootNode];});

    }

    // Need to wait here until createTreeFromNode is finished.
    solveButton.enabled=YES;
    if (numSolutions == 0) {
    solveButton.title = @"Not Solvable";
    } else {
        solveButton.title = @"Solve Puzzle";
    }
}

需要在后台运行才能更新 UI:

Needs to run in background so UI can be updated:

-(void)createTreeFromNode:(TreeNode *)node
{
   // Tried using GCD
   dispatch_queue_t main_queue = dispatch_get_main_queue();

 ...Create Tree Node and find Children Code...

if (!solutionFound){
    // Solution not found yet so check other children by recursion.
   [self createTreeFromNode:newChild];
   } else {
   // Solution found.
   numSolutions ++;
   if (maxMoves < newChild.numberOfMoves) {
       maxMoves = newChild.numberOfMoves;
    }
    if (minMoves < 1 || minMoves > newChild.numberOfMoves) {
        solutionNode = newChild;
        minMoves = newChild.numberOfMoves;

        // Update UI on main Thread

        dispatch_async(main_queue, ^{
                        minMovesLabel.stringValue = [NSString stringWithFormat:@"%d",minMoves];
                        numSolutionsLabel.stringValue = [NSString stringWithFormat:@"%d",numSolutions];
                        maxMovesLabel.stringValue = [NSString stringWithFormat:@"%d",maxMoves];
                    });
                }                        

推荐答案

GCD 和 performSelectorInBackground 示例如下.但首先,让我们看看您的代码.

GCD and performSelectorInBackground samples below. But first, let's look at your code.

你不能在上面的代码中等待你想要的地方.这是你的代码.你在评论中说等待是不正确的.看看我在哪里加了 NO.

You cannot wait where you want to in the code above. Here's the code you had. Where you say wait in the comment is incorrect. See where I added NO.

- (void) solvePuzzle:(id)sender{
    solveButton.enabled = NO;
    solveButton.title = @"Working . . . .";

    // I've tried using this as a Background thread, but I can't get the code to waitTilDone before continuing and changing the button state.
    [self performSelectorInBackground:@selector(createTreeFromNode:) withObject:rootNode];

    // NO - do not wait or enable here.
    // Need to wait here until createTreeFromNode is finished.
    solveButton.enabled=YES;

}

UI 消息循环在主线程上运行,以保持 UI 运行.solvePuzzle 在主线程上被调用,所以你不能等待 - 它会阻塞 UI.它也无法将按钮设置回启用状态 - 工作尚未完成.

A UI message loop is running on the main thread which keeps the UI running. solvePuzzle is getting called on the main thread so you can't wait - it will block the UI. It also can't set the button back to enabled - the work hasn't been done yet.

工作函数在后台线程上的工作是完成工作,然后在完成后更新 UI.但是您不能从后台线程更新 UI.如果您不使用块而是使用 performSelectInBackground,那么在您完成后,调用 performSelectorOnMainThread,它会调用一个选择器来更新您的 UI.

It is the worker function's job on the background thread to do the work and then when it's done to then update the UI. But you cannot update the UI from a background thread. If you're not using blocks and using performSelectInBackground, then when you're done, call performSelectorOnMainThread which calls a selector to update your UI.

performSelectorInBackground 示例:

在这个代码段中,我有一个按钮来调用长时间运行的工作,一个状态标签,我添加了一个滑块来显示我可以在 bg 工作完成时移动滑块.

In this snippet, I have a button which invokes the long running work, a status label, and I added a slider to show I can move the slider while the bg work is done.

// on click of button
- (IBAction)doWork:(id)sender
{
    [[self feedbackLabel] setText:@"Working ..."];
    [[self doWorkButton] setEnabled:NO];

    [self performSelectorInBackground:@selector(performLongRunningWork:) withObject:nil];
}

- (void)performLongRunningWork:(id)obj
{
    // simulate 5 seconds of work
    // I added a slider to the form - I can slide it back and forth during the 5 sec.
    sleep(5);
    [self performSelectorOnMainThread:@selector(workDone:) withObject:nil waitUntilDone:YES];
}

- (void)workDone:(id)obj
{
    [[self feedbackLabel] setText:@"Done ..."];
    [[self doWorkButton] setEnabled:YES];
}

GCD 示例:

// on click of button
- (IBAction)doWork:(id)sender
{
    [[self feedbackLabel] setText:@"Working ..."];
    [[self doWorkButton] setEnabled:NO];

    // async queue for bg work
    // main queue for updating ui on main thread
    dispatch_queue_t queue = dispatch_queue_create("com.sample", 0);
    dispatch_queue_t main = dispatch_get_main_queue();

    //  do the long running work in bg async queue
    // within that, call to update UI on main thread.
    dispatch_async(queue, 
                   ^{ 
                       [self performLongRunningWork]; 
                       dispatch_async(main, ^{ [self workDone]; });
                   });    
}

- (void)performLongRunningWork
{
    // simulate 5 seconds of work
    // I added a slider to the form - I can slide it back and forth during the 5 sec.
    sleep(5);
}

- (void)workDone
{
    [[self feedbackLabel] setText:@"Done ..."];
    [[self doWorkButton] setEnabled:YES];
}

这篇关于GCD、线程、程序流和 UI 更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆