在我的应用程序中,我在NSOperationQueue中执行10个异步NSURLConnections作为NSInvocationOperations。为了防止每个操作在连接有机会完成之前返回,我可以调用CFRunLoopRun(),如下所示:
- (void)connectInBackground:(NSURLRequest*)URLRequest {
TTURLConnection* connection = [[TTURLConnection alloc] initWithRequest:URLRequest delegate:self];
// Prevent the thread from exiting while the asynchronous connection completes the work. Delegate methods will
// continue the run loop when the connection is finished.
CFRunLoopRun();
[connection release];
}
连接完成后,最终连接委托选择器调用CFRunLoopStop(CFRunLoopGetCurrent())以恢复connectInBackground()中的执行,允许它正常返回:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
...
// Resume execution where CFRunLoopRun() was called.
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
...
// Resume execution where CFRunLoopRun() was called.
CFRunLoopStop(CFRunLoopGetCurrent());
}
这很好用,它是线程安全的,因为我将每个连接的响应和数据作为实例变量捆绑在TTURLConnection子类中。
NSOperationQueue声称将其最大并发操作数保留为NSOperationQueueDefaultMaxConcurrentOperationCount允许它动态调整操作数,但是,在这种情况下,它总是判定1就足够了。由于这不是我想要的,我已将最大数量更改为10,现在它非常重要。
这个问题是这些线程(在SpringBoard和DTMobileIS的帮助下)占用了所有可用的CPU时间并导致主线程变得潜伏。换句话说,一旦CPU 100%被利用,主线程就不会像它需要的那样快地处理UI事件,以便维持平滑的UI。具体来说,表格视图滚动变得紧张。
Process Name % CPU
SpringBoard 45.1
MyApp 33.8
DTMobileIS 12.2
...
当用户与屏幕交互或者表滚动时,主线程的优先级变为1.0(最高可能),其运行循环模式变为UIEventTrackingMode。默认情况下,每个操作的线程都是0.5优先级,异步连接在NSDefaultRunLoopMode中运行。由于我对线程及其运行循环如何根据优先级和模式进行交互的理解有限,我感到难过。
有没有办法在我的应用程序的后台线程中安全地消耗所有可用的CPU时间,同时仍然保证其主线程被给予尽可能多的CPU?也许通过强制主线程尽可能多地运行? (我认为线程优先级会照顾到这一点。)
更新12月23日: 我终于开始处理CPU采样器,并找到了UI变得紧张的大部分原因。首先,我的软件调用了一个具有互斥信号量的库。这些锁在短时间内阻塞了主线程,导致滚动略有跳过。
此外,我发现了一些昂贵的NSFileManager调用和md5散列函数,这些函数花费了太多时间来运行。过于频繁地分配大对象会导致主线程中出现其他一些性能命中。
我已经开始解决这些问题了,性能已经比以前好多了。我有5个同时连接,滚动很顺利,但我还有更多的工作要做。我打算编写一个指南,介绍如何使用CPU Sampler来检测和修复影响主线程性能的问题。感谢您的评论到目前为止,他们很有帮助!
更新2010年1月14日: 在达到可接受的性能后,我开始意识到CFNetwork框架偶尔会泄漏内存。例外情况随机(但很少)在CFNetwork内部被提升!我尽我所能避免这些问题,但没有任何效果。我很确定这些问题是由NSURLConnection本身的缺陷造成的。我编写的测试程序除了运动NSURLConnection之外什么也没做,而且它们仍在崩溃和泄漏。
最终我用ASIHTTPRequest取代了NSURLConnection,并且崩溃完全停止了。 CFNetwork 几乎从不泄漏,但是,在解析DNS名称时仍然会发生一个非常罕见的泄漏。我现在很满意。希望这些信息能为您节省一些时间!
答案 0 :(得分:7)
在实践中,您不能拥有超过两个或三个后台网络线程,并且让UI保持完全响应。
优化用户响应能力,这是用户真正注意到的唯一事情。或者(我真的不愿意这样说)在你的应用程序中添加一个“Turbo”按钮,它会建立一个非交互式模态对话框,并在启动时将并发操作增加到10个。
答案 1 :(得分:3)
听起来似乎NSOperationQueueDefaultMaxConcurrentOperationCount
被设置为1是有原因的!我觉得你的手机很糟糕。您可能能够处理线程优先级 - 我认为Mach核心是可用的并且是官方祝福的API的一部分 - 但对我来说这听起来像是错误的方法。
使用“系统”常量的一个优点是Apple可以为您调整应用程序。你打算如何调整它以在原始iPhone上运行?对于明年的四核iPhone来说足够高吗?
答案 2 :(得分:0)
詹姆斯,虽然我没有遇到过您的问题,但我取得的成功是使用同步连接在NSOperation
子类中进行下载。
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];
我使用此方法从网络位置抓取图像资源并更新目标UIImageView
。下载发生在NSOperationQueue
中,更新图像视图的方法在主线程上执行。