iphone - GCD、线程、程序流程和 UI 更新

标签 iphone objective-c ios multithreading macos

我很难弄清楚如何将它们组合在一起。 我在 Mac 上有一个解谜应用程序。 你输入谜题,按下一个按钮,当它试图找到解决方案的数量时, 最小移动等我想保持 UI 更新。 然后在完成计算后,重新启用按钮并更改标题。

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

基本上,用户按下一个按钮,该按钮启用=否,调用函数来计算拼图。在计算时,使用移动/解决方案数据更新 UI 标签。 然后一旦它完成了拼图的计算,Button 就启用了=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:

-(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 示例。但首先,让我们看看您的代码。

您不能在上面的代码中等待您想要的地方。 这是你的代码。你在评论中说等待的地方是不正确的。看我在哪里加了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。它也无法将按钮设置回启用状态 - 工作尚未完成。

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

performSelectorInBackground 示例:

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

// 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];
}

关于iphone - GCD、线程、程序流程和 UI 更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7290931/

相关文章:

iPhone ansi c : pointer being freed was not allocated

苹果手机 : embed a fully integrated customized (Google) map in a native app

iphone - 有没有办法让文本字段条目必须是电子邮件? (在 xcode 中)

ios - UIBarButtonItem:针对目标操作调用与 PerformSelector:withObject 的帧处理方式不同:

ios - 无法在连接的自定义 UITableViewCell 类中创建 socket

iphone - 将同一对象添加到多个对多关系

iphone - iPhone 上的本地与远程套接字连接

ios - 制作一个 iOS 框架 : including 3rd party libraries and code

iphone - 如何在操作表中添加键盘

ios - 将核心数据中的 UIView 子类或 UIBezierPath 存储为可转换