我有一个while
循环,运行了很多秒,这就是为什么我想在该过程中更新进度条(NSProgressIndicator),但它在循环结束后只更新一次。顺便说一句,如果我想要更新标签文本,也会发生同样的情况。
我相信,我的循环会阻止该应用程序的其他事情发生。必须有另一种技术。这与线程有什么关系吗?我是在正确的轨道上吗?有人可以给我一个简单的例子,如何“优化”我的应用程序?
我的应用程序是一个Cocoa应用程序(Xcode 3.2.1),在我的Example_AppDelegate.m
中有这两种方法:
// This method runs when a start button is clicked. - (IBAction)startIt:(id)sender { [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 [progressbar setDoubleValue:progr]; [progressbar needsDisplay]; // Do I need this? // Do some more hard work here... } } // This method runs when a stop button is clicked, but as long // as -startIt is busy, a click on the stop button does nothing. - (IBAction)stopIt:(id)sender { NSLog(@"Stop it!"); running = NO; [progressbar stopAnimation:sender]; }
我是Objective-C,Cocoa和带UI的应用程序的新手。非常感谢您提供任何有用的答案。
答案 0 :(得分:15)
如果您正在为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);
}
答案 1 :(得分:3)
您可以尝试此代码..
[progressbar setUsesThreadedAnimation:YES];
答案 2 :(得分:2)
我相信,我的循环会阻止该应用程序的其他事情发生。
正确。你需要以某种方式解决这个问题。
一种方法是计时器,你可以在计时器回调中一次一点地缩小队列。另一种方法是将代码包装起来处理NSOperation子类中的一个项目,并创建该类的实例(操作)并将它们放入NSOperationQueue中。
这与线程有什么关系吗?
不一定。 NSOperations在线程上运行,但NSOperationQueue将处理为您生成线程。计时器是一种单线程解决方案:每个计时器都在您计划的线程上运行。这可能是一个优点或缺点 - 你决定。
有关详细信息,请参阅the threads section of my intro to Cocoa。
答案 3 :(得分:2)
这对我有用,这是其他人的答案的组合,这些答案对我们来说似乎不起作用(至少对我而言):
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
});