NSURLSession 和 NSDefaultRunLoopMode
Posted
技术标签:
【中文标题】NSURLSession 和 NSDefaultRunLoopMode【英文标题】:NSURLSession and NSDefaultRunLoopMode 【发布时间】:2013-11-20 14:07:47 【问题描述】:使用NSURLConnection
时,您可以选择使用NSRunLoop
安排连接:
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode
如果用户滚动,传递NSDefaultRunLoopMode
将有效地导致连接暂停,这对性能很有好处,因为用户体验从未受到下载的影响。
有没有办法让NSURLSession
出现类似的行为?我已阅读文档并尝试了各种配置会话的方法,但均未成功。
【问题讨论】:
【参考方案1】:NSURLSession
在“上层”工作,对开发人员来说比使用NSURLConnection
更简单。
我做了一些测试,我认为不可能控制 NSURLSession
的运行循环和模式,因为它们似乎是由外部守护进程而不是你的应用程序管理的(我只用 NSURLSessionDownloadTask
进行了测试)。
做这个简单的测试:
-
下载并执行this Github project
开始下载
打开“下载”控制器查看下载状态
暂停应用
等一下
取消暂停应用
您会看到在您的应用暂停时继续下载,因此当您启动 NSURLSession
时,控制权会传递给系统,在您的应用之外:这意味着工作的主要部分没有发生在内部 runLoop 中。
您唯一可以控制的是调度委托调用的串行队列(传递回您的应用程序)。委托调用排队等待执行(在主线程或后台线程上,您可以选择它),因为NSOperationQueue
使用 Grand Central Dispatch 将调用排队,我不确定用于此的运行循环模式,但是我认为这是继续您的研究的一个很好的起点。
编辑: 如果我没记错的话,在后台线程上进行的调度调用是在没有运行 runloop 的线程上进行的。其实如果加这行
NSLog(@"%@", [[NSRunLoop currentRunLoop] currentMode]);
在上一个项目中FLDownloader
类的一个委托方法上,你会看到没有运行模式(nil),当runloop没有运行时会发生这种情况。
【讨论】:
您正在使用后台会话配置,这导致您的任务在进程外进行管理。非后台会话在应用程序管理的线程上运行。我同意似乎不可能做我想做的事。 我不确定这个,但我很好奇这个论点并做了一些研究。如果你分析一个新创建的 NSURLSession(非后台)的整个堆栈跟踪,有一些方法调用属于 runloops。例如[NSURLConnection, _resourceLoadLoop:]
和[NSURLConnection, resourceLoaderRunLoop]
。我试图调配第二个,它让我可以访问 CFRunLoop 对象(作为返回值)。也许你可以尝试用它作弊并改变 NSURLSession 的模式(如果我理解正确,似乎 NSURLSession 使用 kCFRunLoopDefaultMode)
有趣的是,我也在跟踪中看到了 resourceLoadLoop,但没有进一步研究它。不过,我不想对此进行破解,如果不支持,我将坚持在默认模式下运行的标准行为。【参考方案2】:
如果有人再次遇到这个问题。您不能将下载任务安排到不同的运行循环中,但您可以在不同的运行循环模式下处理响应,这仍然大大提高了滚动时的性能。
[self performSelectorOnMainThread:@selector(requestDidFinishLoadingWithData:)
withObject:data
waitUntilDone:YES
modes:@[NSDefaultRunLoopMode]];
【讨论】:
以上是关于NSURLSession 和 NSDefaultRunLoopMode的主要内容,如果未能解决你的问题,请参考以下文章
NSURLSession 委托跨类拆分 - NSURLSession、NSURLUploadTask、NSURLDownloadTask