我是一位经验丰富的C / C ++程序员,正在加速iPhone上的Objective C。我已经做了很多搜索,但没有找到一个令人满意的答案,一定是一个常见的问题;如果在其他地方得到解答,我道歉,我们将不胜感激。
我的应用程序非常耗费CPU。 UI具有显示进度的简单显示和开始/停止按钮。分配最多可能的CPU周期以完成工作的最佳方法是什么,同时仍然确保显示器定期更新并且启动/停止按钮响应?我已经读过你不应该在主线程中工作,但除此之外我还没有找到很多建议。鉴于此,我已经在NSOperation队列中实现了我的工作。我还将屏幕刷新放在自己的队列中。我还用NSThread sleepForTimeIntervals自由地散布了代码。我已经尝试了从.001到1的不同睡眠时间(例如[NSThread sleepForTimeIntervals .1])。尽管如此,屏幕显示最多是缓慢(10秒)并且按下停止按钮突出显示按钮但是没有任何事情再次发生10秒。
1。)NSOperation队列是否合理?如果没有,还有什么? 2.)我如何最小化睡眠? (显然我希望工作能够获得尽可能多的周期/合理,并且我不确定我的睡眠对所有要更新的UI都做了什么。) 3.)是否有更好的技术来保持UI的最新状态?例如,我可以使用NSTimer或其他方法向UI发送消息,告诉它更新和/或检查按钮的状态吗?
感谢您的支持。
答案 0 :(得分:5)
1。)NSOperation队列是否合理?如果没有,还有什么?
NSOperationQueue听起来很合理。
当然,您可以选择:pthreads,libdispatch(又名GCD),构建在pthreads之上的c ++线程库等等,如果你没有产生很多/很多,那么它只是归结为你赞成的模特。
2.。)如何最大限度地减少睡眠? (显然我希望工作能够获得尽可能多的周期/合理,并且我不确定我的睡眠对所有要更新的UI都做了什么。)
不要睡觉=)您可以为您的ui元素使用计时器,或者使用显式回调或通知来通知依赖项。如果依赖项执行ui更新,那么您可能会将消息添加到主线程的消息队列中。
3。)是否有更好的技术来保持UI的最新状态?例如,我可以使用NSTimer或其他方法向UI发送消息,告诉它更新和/或检查按钮的状态吗?
这真的取决于你在做什么。如果您只想更新进度条,则可以从辅助线程写入值并从主线程中读取值。然后在主运行循环上使用计时器定期向对象发送消息以更新其显示(基于当前值)。对于类似未分级进度指示器的东西,这可能是好的。
另一个替代方案对于事件或阶段更有用:它将涉及在进行过程时从辅助线程发布更新(例如,向委托发送通知或回调)(#2下的更多信息)。
<强>更新强>
是的,没关系 - 你可以采取许多措施。 “最佳”取决于具体情况。我不确定这在iOS模型中是否合适,但听起来确实如此。
我目前的理解是在一个线程(不是主要的!)中启动UI,
你真的没有明确启动UI;通常通过将事件和消息推送到主线程来驱动主线程。主线程使用运行循环并在运行循环的每次迭代中处理排队的消息/事件。您还可以在将来安排这些消息(稍后会详细介绍)。话虽如此,你对UIKit和AppKit(如果你的目标是osx)对象的所有消息都应该在主线程上(作为一种概括,你最终会知道这有例外)。如果你有一个特定的实现,它与消息传递UIKit对象的方法完全分开,并且该程序是线程安全的,那么你可以从任何线程实际执行这些消息,因为它不会影响UIKit实现的状态。最简单的例子:
@interface MONView : UIView
@end
@implementation MONView
// ...
- (NSString *)iconImageName { return @"tortoise.png"; } // pure and threadsafe
@end
启动我的工作线程,使用计时器为UI生成一个信号,以查看进度值并相应地更新进度条。出于这个特定应用的目的,你的倒数第二段是充足的,我不需要去最后一段的长度(至少现在)。谢谢。
要做到这一点,你可以使用类似的方法:
@interface MONView : UIView
{
NSTimer * timer;
MONAsyncWorker * worker; // << this would be your NSOperation subclass, if you use NSOperation.
}
@end
@implementation MONView
// callback for the operation 'worker' when it completes or is cancelled.
- (void)workerWillExit
{
assert([NSThread isMainThread]); // call on main
// end recurring updates
[self.timer invalidate];
self.timer = nil;
// grab what we need from the worker
self.worker = nil;
// update ui
}
// timer callback
- (void)timerUpdateCallback
{
assert([NSThread isMainThread]); // call on main
assert(self.worker);
double progress = self.worker.progress;
[self updateProgressBar:progress];
}
// controller entry to initiate an operation
- (void)beginDownload:(NSURL *)url
{
assert([NSThread isMainThread]); // call on main
assert(nil == worker); // call only once in view's lifetime
// create worker
worker = [[MONAsyncWorker alloc] initWithURL:url];
[self.operationQueue addOperation:worker];
// configure timer
const NSTimeInterval displayUpdateFrequencyInSeconds = 0.200;
timer = [[NSTimer scheduledTimerWithTimeInterval:displayUpdateFrequencyInSeconds target:self selector:@selector(timerUpdateCallback) userInfo:nil repeats:YES] retain];
}
@end
请注意,这是一个非常原始的演示。将计时器,更新处理和操作放在视图的控制器中,而不是视图中也更常见。
答案 1 :(得分:3)
您是否正在主线程上进行UI更新?这非常重要,因为UIKit不是线程安全的,并且从辅助线程使用它可能导致行为迟缓(或者崩溃)。您通常不需要在后台线程/队列中使用sleep
以使UI保持响应(除非您的UI本身非常占用CPU,但在这里似乎并非如此)。
如果在主线程上使用
之类的东西运行,您可以检查更新UI的任何方法NSAssert([NSThread isMainThread], @"UI update not running on main thread");
将UI更新与主线程同步的简单轻量方法是使用Grand Central Dispatch:
dispatch_async(dispatch_get_main_queue(), ^ {
//do your UI updates here...
});
答案 2 :(得分:2)
在这里,您可以回答我的问题。
1)由于您是一位经验丰富的C程序员,因此您会对Grand Central Dispatch(GCD)感到满意,这是一种基于C的并发API。
2)使用GCD,您根本不需要睡觉。只需使用最大优先级(DISPATCH_QUEUE_PRIORITY_HIGH)异步调度您需要在队列中完成的工作。
3)当您需要更新UI时,只需根据需要在主队列(在同一个块中完成工作,使用dispatch_get_main_queue())调度UI更新。
查看相关的GCD文档here。
答案 3 :(得分:1)
我有一个模型对象来执行CPU任务,它具有输出更改时的委托回调和视图控制器。在viewDidLoad
中,您将视图控制器设置为模型的委托。因此,当计算的数据已更新时,模型可以使用线程并在主队列上发回消息。除非您的案例特别复杂,否则只需使用Grand Central Dispatch并将密集型任务调度发送到另一个线程。
当然,你不应该在任何地方调用sleepForTimeInterval来实现你想要的目标。