【问题标题】:How do I update a progress bar in Cocoa during a long running loop?如何在长时间运行的循环中更新 Cocoa 中的进度条?
【发布时间】:2011-01-31 08:43:49
【问题描述】:

我有一个while 循环,它运行了很多秒,这就是为什么我想在这个过程中更新一个进度条(NSProgressIndicator),但它只在循环完成后更新一次。顺便说一句,如果我想更新标签文本,也会发生同样的情况。

我相信,我的循环会阻止该应用程序的其他事情发生。必须有另一种技术。这与线程或其他东西有关吗?我在正确的轨道上吗?谁能给我一个简单的例子,如何“优化”我的应用程序?

我的应用程序是一个 Cocoa 应用程序 (Xcode 3.2.1),在我的 Example_AppDelegate.m 中有这两种方法:

// 此方法在单击开始按钮时运行。 - (IBAction)startIt:(id)sender { [进度条设置双值:0.0]; [进度条开始动画:发件人]; 运行=是; // 这是一个实例变量 诠释 i = 0; 在跑步的时候) { if (i++ >= processAmount) { // processAmount 类似于 1000000 运行 = 否; 继续; } // 更新进度条 双程序=(双)我/(双)processAmount; NSLog(@"progr: %f", progr); // 记录 0.0 到 1.0 之间的值 [进度条 setDoubleValue:progr]; [进度条需要显示]; // 我需要这个吗? // 在这里做一些更努力的工作... } } // 这个方法在点击停止按钮时运行,但只要 // 因为 -startIt 很忙,所以单击停止按钮什么也不做。 - (IBAction)stopIt:(id)sender { NSLog(@"停止!"); 运行 = 否; [进度条停止动画:发件人]; }

我对 Objective-C、Cocoa 和带有 UI 的应用程序非常陌生。非常感谢您提供任何有用的答案。

【问题讨论】:

    标签: objective-c cocoa macos


    【解决方案1】:

    如果您正在为 Snow Leopard 构建,我认为最简单的解决方案是使用积木和 Grand Central Dispatch。

    以下代码向您展示了使用 GCD 时 startIt: 方法的外观。

    您的stopIt: 方法在您编写时应该可以正常工作。它之前不起作用的原因是鼠标事件发生在主线程上,因此按钮没有响应您,因为您正在主线程上工作。这个问题现在应该已经解决了,因为现在使用 GCD 已经将工作放在不同的线程上。试一下代码,如果不行,请告诉我,我会看看我是否在其中犯了一些错误。

    // This method runs when a start button is clicked.
    - (IBAction)startIt:(id)sender {
    
        //Create the block that we wish to run on a different thread.
        void (^progressBlock)(void);
        progressBlock = ^{
    
        [progressbar setDoubleValue:0.0];
        [progressbar startAnimation:sender];
        running = YES; // this is a instance variable
    
        int i = 0;
        while (running) {
            if (i++ >= processAmount) { // processAmount is something like 1000000
                running = NO;
                continue;
            }
    
            // Update progress bar
            double progr = (double)i / (double)processAmount;
            NSLog(@"progr: %f", progr); // Logs values between 0.0 and 1.0
    
            //NOTE: It is important to let all UI updates occur on the main thread,
            //so we put the following UI updates on the main queue.
            dispatch_async(dispatch_get_main_queue(), ^{
                [progressbar setDoubleValue:progr];
                [progressbar setNeedsDisplay:YES];
            });
    
            // Do some more hard work here...
        }
    
        }; //end of progressBlock
    
        //Finally, run the block on a different thread.
        dispatch_queue_t queue = dispatch_get_global_queue(0,0);
        dispatch_async(queue,progressBlock);
    }
    

    【讨论】:

    • 非常感谢,这个周末我试试看!
    • 这就是我想要的。工作正常,这给了我一个很好的起点来了解更多。谢谢!
    • 感谢这个辣酱玉米饼馅!我刚刚学到了一些我不知道的东西!很高兴了解积木和 GCD!
    【解决方案2】:

    你可以试试这个代码..

    [progressbar setUsesThreadedAnimation:YES];
    

    【讨论】:

      【解决方案3】:

      我相信,我的循环阻止了该应用程序的其他事情发生。

      正确。你需要以某种方式打破它。

      一种方法是使用计时器,您可以在计时器回调中一次一点地减少队列。另一种方法是包装代码以处理 NSOperation 子类中的一项,并创建该类(操作)的实例并将它们放入 NSOperationQueue。

      这和线程有关吗?

      不一定。 NSOperations 在线程上运行,但 NSOperationQueue 将为您处理生成线程。计时器是一种单线程解决方案:每个计时器都在您安排它的线程上运行。这可能是优势也可能是劣势——由您决定。

      更多详情请见the threads section of my intro to Cocoa

      【讨论】:

        【解决方案4】:

        这对我有用,这是其他人的答案的组合,这些答案似乎对我自己不起作用(至少对我而言):

        dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
          //do something
          dispatch_async(dispatch_get_main_queue(), ^{
            progressBar.progress = (double)x / (double)[stockList count];            
          });  
          //do something else
        });
        

        【讨论】:

          猜你喜欢
          • 2014-05-26
          • 1970-01-01
          • 2011-05-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-07-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多