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

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

问题描述

我很难弄清楚如何把这些放在一起。
我在mac上有一个解谜应用程序。
您输入拼图,按一个按钮,当它试图找到解决方案的数量时,
min移动,这样我想保持UI更新。
然后一旦完成计算,重新启用按钮并更改标题。

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标签更新。
然后一旦完成计算拼图,Button就会被启用= 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天全站免登陆