ios 如何将 NSOperationQueue 与 NSURLSession 一起使用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21918722/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I use NSOperationQueue with NSURLSession?
提问by Doug Smith
I'm trying to build a bulk image downloader, where images can be added to a queue on the fly to be downloaded, and I can find out the progress and when they're done downloading.
我正在尝试构建一个批量图像下载器,可以将图像动态添加到队列中进行下载,我可以找出进度以及何时完成下载。
Through my reading it seems like NSOperationQueue
for the queue functionality and NSURLSession
for the network functionality seems like my best bet, but I'm confused as to how to use the two in tandem.
通过我的阅读,NSOperationQueue
队列功能和NSURLSession
网络功能似乎是我最好的选择,但我对如何同时使用两者感到困惑。
I know I add instances of NSOperation
to the NSOperationQueue
and they get queued. And it seems I create a download task with NSURLSessionDownloadTask
, and multiple if I need multiple tasks, but I'm not sure how I put the two together.
我知道我将 的实例添加NSOperation
到NSOperationQueue
并且它们被排队。似乎我创建了一个下载任务NSURLSessionDownloadTask
,如果我需要多个任务,则创建多个任务,但我不确定如何将两者放在一起。
NSURLSessionDownloadTaskDelegate
seems to have all the information I need for download progress and completion notifications, but I also need to be able to stop a specific download, stop all the downloads, and deal with the data I get back from the download.
NSURLSessionDownloadTaskDelegate
似乎拥有下载进度和完成通知所需的所有信息,但我还需要能够停止特定下载、停止所有下载并处理从下载中返回的数据。
采纳答案by Rob
Your intuition here is correct. If issuing many requests, having an NSOperationQueue
with maxConcurrentOperationCount
of 4 or 5 can be very useful. In the absence of that, if you issue many requests (say, 50 large images), you can suffer timeout problems when working on a slow network connection (e.g. some cellular connections). Operation queues have other advantages, too (e.g. dependencies, assigning priorities, etc.), but controlling the degree of concurrency is the key benefit, IMHO.
你在这里的直觉是正确的。如果发出许多请求,NSOperationQueue
使用maxConcurrentOperationCount
4 或 5 可能非常有用。否则,如果您发出许多请求(例如,50 张大图像),则在处理慢速网络连接(例如某些蜂窝连接)时可能会遇到超时问题。操作队列也有其他优点(例如依赖关系、分配优先级等),但恕我直言,控制并发度是关键的好处。
If you are using completionHandler
based requests, implementing operation-based solution is pretty trivial (it's the typical concurrent NSOperation
subclass implementation; see the Configuring Operations for Concurrent Executionsection of the Operation Queueschapter of the Concurrency Programming Guidefor more information).
如果您使用completionHandler
基于请求,实现基于操作的解决方案非常简单(这是典型的并发NSOperation
子类实现;有关更多信息,请参阅并发编程指南的操作队列一章的并发执行配置操作部分)。
If you are using the delegate
based implementation, things start to get pretty hairy pretty quickly, though. This is because of an understandable (but incredibly annoying) feature of NSURLSession
whereby the task-level delegates are implemented at the session-level. (Think about that: Two different requests that require different handling are calling the same delegate method on the shared session object. Egad!)
但是,如果您正在使用delegate
基于实现的实现,事情很快就会变得非常棘手。这是因为NSURLSession
任务级委托在会话级实现的一个可以理解(但非常烦人)的特性。(想一想:需要不同处理的两个不同请求在共享会话对象上调用相同的委托方法。Egad!)
Wrapping a delegate-based NSURLSessionTask
in an operation can be done (I, and others I'm sure, have done it), but it involves an unwieldy process of having the session object maintain a dictionary cross referencing task identifiers with task operation objects, have it pass these task delegate methods passed to the task object, and then have the task objects conform to the various NSURLSessionTask
delegate protocols. It's a pretty significant amount of work required because NSURLSession
doesn't provide a maxConcurrentOperationCount
-style feature on the session (to say nothing of other NSOperationQueue
goodness, like dependencies, completion blocks, etc.).
NSURLSessionTask
可以在操作中包装基于委托的操作(我和我确定的其他人已经完成了),但它涉及一个笨拙的过程,即让会话对象维护字典交叉引用任务标识符与任务操作对象,有它将这些任务委托方法传递给任务对象,然后使任务对象符合各种NSURLSessionTask
委托协议。这是一项非常重要的工作,因为NSURLSession
它没有maxConcurrentOperationCount
在会话中提供-style 功能(更不用说其他NSOperationQueue
优点,例如依赖项、完成块等)。
And it's worth pointing out that operation-based implementation is a bit of a non-starter with background sessions, though. Your upload/download tasks will continue to operate well after the app has been terminated (which is a good thing, that's fairly essential behavior in a background request), but when your app is restarted, the operation queue and all of its operations are gone. So you have to use a pure delegate-based NSURLSession
implementation for background sessions.
值得指出的是,基于操作的实现对于后台会话来说有点不可思议。在应用程序终止后,您的上传/下载任务将继续正常运行(这是一件好事,这是后台请求中相当重要的行为),但是当您的应用程序重新启动时,操作队列及其所有操作都消失了. 因此,您必须NSURLSession
对后台会话使用纯基于委托的实现。
回答by Max MacLeod
Conceptually, NSURLSession is an operation queue. If you resume an NSURLSession task and breakpoint on the completion handler, the stack trace can be quite revealing.
从概念上讲,NSURLSession 是一个操作队列。如果您在完成处理程序上恢复 NSURLSession 任务和断点,则堆栈跟踪可能非常具有启发性。
Here's an excerpt from the ever faithful Ray Wenderlich's tutorial on NSURLSessionwith an added NSLog
statement to breakpoint on executing the completion handler:
这是从忠实的 Ray Wenderlich 关于 NSURLSession 的教程中摘录的,其中添加了一条NSLog
语句来断点执行完成处理程序:
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:londonWeatherUrl]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
// handle response
NSLog(@"Handle response"); // <-- breakpoint here
}] resume];
Above, we can see the completion handler being executed in Thread 5 Queue: NSOperationQueue Serial Queue
.
上面,我们可以看到在Thread 5 Queue: NSOperationQueue Serial Queue
.
So, my guess is that each NSURLSession maintains it's own operation queue, and each task added to a session is - under the hood - executed as an NSOperation. Therefore, it doesn't make sense to maintain an operation queue that controls NSURLSession objects or NSURLSession tasks.
所以,我的猜测是每个 NSURLSession 都维护它自己的操作队列,并且添加到会话中的每个任务 - 在幕后 - 作为 NSOperation 执行。因此,维护一个控制 NSURLSession 对象或 NSURLSession 任务的操作队列是没有意义的。
NSURLSessionTask itself already offers equivalent methods such as cancel
, resume
, suspend
, and so on.
NSURLSessionTask 本身已经提供了等效的方法,例如cancel
、resume
、suspend
等。
It's true that there is less control than you would have with your own NSOperationQueue. But then again, NSURLSession is a new class the purpose of which is undoubtably to relieve you of that burden.
确实,与使用自己的 NSOperationQueue 相比,控制要少。但话又说回来,NSURLSession 是一个新类,其目的无疑是为了减轻您的负担。
Bottom line: if you want less hassle - but less control - and trust Apple to perform the network tasks competently on your behalf, use NSURLSession. Otherwise, roll your own with NSURLConnection and your own operation queues.
底线:如果你想要更少的麻烦 - 但更少的控制 - 并相信 Apple 能够代表你胜任地执行网络任务,请使用 NSURLSession。否则,使用 NSURLConnection 和您自己的操作队列推出您自己的。
回答by yageek
Update:The executing
and finishing
properties hold the knowledge about the status of the current NSOperation
. Once you finishing
is set to YES
and executing
to NO
, your operation is considered as finished. The correct way of dealing with it does not require a dispatch_group
and can simply be written as an asynchronous NSOperation
:
更新:在executing
和finishing
性质保存有关当前状态的知识NSOperation
。一旦finishing
被设定为YES
与executing
对NO
,您的操作被视为完成。处理它的正确方法不需要 adispatch_group
并且可以简单地编写为 asynchronous NSOperation
:
- (BOOL) isAsynchronous {
return YES;
}
- (void) main
{
// We are starting everything
self.executing = YES;
self.finished = NO;
NSURLSession * session = [NSURLSession sharedInstance];
NSURL *url = [NSURL URLWithString:@"http://someurl"];
NSURLSessionDataTask * dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
/* Do your stuff here */
NSLog("Will show in second");
self.executing = NO;
self.finished = YES;
}];
[dataTask resume]
}
The term asynchronous
is quite misleading and does not refers to the differentiation between UI (main) thread and background thread.
该术语asynchronous
具有很大的误导性,并不是指 UI(主)线程和后台线程之间的区别。
If isAsynchronous
is set to YES
, it means that some part of the code is executed asynchronously regarding the main
method. Said differently: an asynchronous call is made inside the main
method and the method will finish after the main method finishes.
如果isAsynchronous
被设置为YES
,它意味着代码的某些部分被关于所述异步执行main
方法。换种说法:在main
方法内部进行异步调用,该方法将在 main 方法完成后完成。
I have some slides about how to handle concurrency on apple os: https://speakerdeck.com/yageek/concurrency-on-darwin.
我有一些关于如何在 Apple os 上处理并发的幻灯片:https: //speakerdeck.com/yageek/concurrency-on-darwin。
Old answer: You could try the dispatch_group_t
. You can think them as retain counter for GCD.
旧答案:您可以尝试dispatch_group_t
. 您可以将它们视为 GCD 的保留计数器。
Imagine the code below in the main
method of your NSOperation
subclass :
想象一下子类main
方法中的以下代码NSOperation
:
- (void) main
{
self.executing = YES;
self.finished = NO;
// Create a group -> value = 0
dispatch_group_t group = dispatch_group_create();
NSURLSession * session = [NSURLSession sharedInstance];
NSURL *url = [NSURL URLWithString:@"http://someurl"];
// Enter the group manually -> Value = Value + 1
dispatch_group_enter(group); ¨
NSURLSessionDataTask * dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
/* Do your stuff here */
NSLog("Will show in first");
//Leave the group manually -> Value = Value - 1
dispatch_group_leave(group);
}];
[dataTask resume];
// Wait for the group's value to equals 0
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog("Will show in second");
self.executing = NO;
self.finished = YES;
}
回答by Reid Main
With NSURLSession you don't manually add any operations to a queue. You use the method - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
on NSURLSession to generate a data task which you then start (by calling the resume method).
使用 NSURLSession,您无需手动向队列添加任何操作。您使用- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
NSURLSession 上的方法生成一个数据任务,然后您开始(通过调用 resume 方法)。
You are allowed to provide the operation queue so you can control the properties of the queue and also use it for other operations if you wanted.
您可以提供操作队列,以便您可以控制队列的属性,并根据需要将其用于其他操作。
Any of the usual actions you would want to take on a NSOperation (i.e. start, pause, stop, resume) you perform on the data task.
您希望对数据任务执行的 NSOperation(即启动、暂停、停止、恢复)执行的任何常规操作。
To queue up 50 images to download you can simply create 50 data tasks which the NSURLSession will properly queue up.
要将 50 个图像排队下载,您只需创建 50 个数据任务,NSURLSession 将正确排队。
回答by Above The Gods
If you're using OperationQueue and don't want each operation to create many simultaneous network requests, you can simply call queue.waitUntilAllOperationsAreFinished() after each operation is added to the queue. They will now only execute after the previous one is completed, significantly reducing the amount of simultaneous network connections.
如果您正在使用 OperationQueue 并且不希望每个操作同时创建许多网络请求,您可以在每个操作添加到队列后简单地调用 queue.waitUntilAllOperationsAreFinished() 。它们现在只会在前一个完成后执行,从而显着减少了同时网络连接的数量。
回答by MacMark
Here's a sample project using NSOperation with NSURLSession: https://github.com/MacMark/Operations-Demo
这是一个使用 NSOperation 和 NSURLSession 的示例项目:https: //github.com/MacMark/Operations-Demo
For background sessions you can resurrect the NSOperation by using the session's identifier in this callback:
对于后台会话,您可以通过在此回调中使用会话的标识符来恢复 NSOperation:
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
// Restore the NSOperation that matches the identifier
// Let the NSOperation call the completionHandler when it finishes
}
回答by hnh
Maybe you are looking for this:
也许你正在寻找这个:
http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/
http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/
It is a bit weird that this isn't 'builtin', but if you want to hook up NSURL stuff with NSOperation's it looks like you have to reuse the runloop in the main thread and make the operation a 'concurrent' one ('concurrent' to the queue).
这不是“内置”有点奇怪,但是如果您想将 NSURL 内容与 NSOperation 连接起来,看起来您必须在主线程中重用 runloop 并使操作成为“并发”操作(“并发” ' 到队列)。
Though in your case - if it's just about plain downloads, with no subsequent, dependent, operations hooked up - I'm not sure what you would gain with using NSOperation.
尽管在您的情况下 - 如果它只是简单的下载,没有后续的、依赖的操作连接 - 我不确定使用 NSOperation 会获得什么。