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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 23:32:10  来源:igfitidea点击:

How do I use NSOperationQueue with NSURLSession?

iosobjective-cnsoperationnsoperationqueuensurlsession

提问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 NSOperationQueuefor the queue functionality and NSURLSessionfor 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 NSOperationto the NSOperationQueueand 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.

我知道我将 的实例添加NSOperationNSOperationQueue并且它们被排队。似乎我创建了一个下载任务NSURLSessionDownloadTask,如果我需要多个任务,则创建多个任务,但我不确定如何将两者放在一起。

NSURLSessionDownloadTaskDelegateseems 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 NSOperationQueuewith maxConcurrentOperationCountof 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使用maxConcurrentOperationCount4 或 5 可能非常有用。否则,如果您发出许多请求(例如,50 张大图像),则在处理慢速网络连接(例如某些蜂窝连接)时可能会遇到超时问题。操作队列也有其他优点(例如依赖关系、分配优先级等),但恕我直言,控制并发度是关键的好处。

If you are using completionHandlerbased requests, implementing operation-based solution is pretty trivial (it's the typical concurrent NSOperationsubclass 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 delegatebased implementation, things start to get pretty hairy pretty quickly, though. This is because of an understandable (but incredibly annoying) feature of NSURLSessionwhereby 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 NSURLSessionTaskin 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 NSURLSessionTaskdelegate protocols. It's a pretty significant amount of work required because NSURLSessiondoesn't provide a maxConcurrentOperationCount-style feature on the session (to say nothing of other NSOperationQueuegoodness, 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 NSURLSessionimplementation 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 NSLogstatement 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];

NSOperationQueue Serial Queue breakpoint

NSOperationQueue 串行队列断点

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 本身已经提供了等效的方法,例如cancelresumesuspend等。

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 executingand finishingproperties hold the knowledge about the status of the current NSOperation. Once you finishingis set to YESand executingto NO, your operation is considered as finished. The correct way of dealing with it does not require a dispatch_groupand can simply be written as an asynchronous NSOperation:

更新:executingfinishing性质保存有关当前状态的知识NSOperation。一旦finishing被设定为YESexecutingNO,您的操作被视为完成。处理它的正确方法不需要 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 asynchronousis quite misleading and does not refers to the differentiation between UI (main) thread and background thread.

该术语asynchronous具有很大的误导性,并不是指 UI(主)线程和后台线程之间的区别。

If isAsynchronousis set to YES, it means that some part of the code is executed asynchronously regarding the mainmethod. Said differently: an asynchronous call is made inside the mainmethod 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 mainmethod of your NSOperationsubclass :

想象一下子类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 *)requeston NSURLSession to generate a data task which you then start (by calling the resume method).

使用 NSURLSession,您无需手动向队列添加任何操作。您使用- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)requestNSURLSession 上的方法生成一个数据任务,然后您开始(通过调用 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 会获得什么。