如何在NSURLSession中使用NSOperationQueue?

Dou*_*ith 53 objective-c nsoperation nsoperationqueue ios nsurlsession

我正在尝试构建一个批量图像下载器,可以将图像动态添加到队列中进行下载,我可以找到进度以及何时完成下载.

通过我的阅读,似乎NSOperationQueue对于队列功能和NSURLSession网络功能似乎是我最好的选择,但我很困惑如何使用这两个串联.

我知道我想补充的情况下,NSOperationNSOperationQueue他们得到排队.似乎我创建了一个下载任务NSURLSessionDownloadTask,如果我需要多个任务,我会创建多个任务,但我不确定如何将两者放在一起.

NSURLSessionDownloadTaskDelegate 似乎拥有下载进度和完成通知所需的所有信息,但我还需要能够停止特定下载,停止所有下载,并处理从下载中获取的数据.

Rob*_*Rob 46

你的直觉是正确的.如果发出许多请求,NSOperationQueue使用maxConcurrentOperationCount4或5可能非常有用.如果没有这种情况,如果您发出许多请求(例如,50个大图像),则在处理慢速网络连接(例如某些蜂窝连接)时可能会遇到超时问题.操作队列也有其他优点(例如依赖关系,分配优先级等),但控制并发度是关键的好处,恕我直言.

如果您正在使用completionHandler基于请求的实现,那么实现基于操作的解决方案非常简单(这是典型的并发NSOperation子类实现; 有关详细信息,请参阅" 并发编程指南"的" 操作队列"一章中的" 配置并发执行操作"部分).

如果你正在使用delegate基于实现的东西,那么事情会很快变得非常毛茸茸.这是因为可以理解(但令人难以置信的烦人)的特性,NSURLSession即任务级代表在会话级实现.(考虑一下:需要不同处理的两个不同请求是在共享会话对象上调用相同的委托方法.Egad!)

NSURLSessionTask在一个操作中包装一个基于委托的代码可以完成(我和其他人,我确定已经完成了),但是它涉及一个笨拙的过程,让会话对象维护一个字典交叉引用任务标识符和任务操作对象,它传递给传递给任务对象的这些任务委托方法,然后使任务对象符合各种NSURLSessionTask委托协议.这需要相当多的工作量,因为NSURLSession它不会maxConcurrentOperationCount在会话中提供一种风格的功能(更不用说其他NSOperationQueue优点,如依赖项,完成块等).

值得指出的是,基于操作的实现对于后台会话来说是一个非启动性的.应用程序终止后,您的上传/下载任务将继续运行良好(这是一件好事,这在后台请求中是相当重要的行为),但是当您的应用程序重新启动时,操作队列及其所有操作都消失了.因此,您必须NSURLSession为后台会话使用纯委托实现.

  • @algal如果没有委托,有许多功能无法使用.例如,大型下载的进度更新,以便您可以更新进度视图.或处理身份验证挑战.或者识别重定向.或者,如果你正在做一些流媒体协议.所以,肯定有时候你想要委托.但对于那些你不这样做的情况,生活会更容易. (3认同)

Max*_*eod 38

从概念上讲,NSURLSession是一个操作队列.如果在完成处理程序上恢复NSURLSession任务和断点,则堆栈跟踪可能非常明显.

这是一篇关于永远忠实的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];
Run Code Online (Sandbox Code Playgroud)

NSOperationQueue串行队列断点

上面,我们可以看到正在执行的完成处理程序Thread 5 Queue: NSOperationQueue Serial Queue.

所以,我的猜测是每个NSURLSession维护它自己的操作队列,并且添加到会话的每个任务都是 - 在引擎盖下 - 作为NSOperation执行.因此,维护一个控制NSURLSession对象或NSURLSession任务的操作队列是没有意义的.

NSURLSessionTask本身已经提供了相当的方法,如cancel,resume,suspend,等.

确实,对你自己的NSOperationQueue的控制比你的控制要少.但话又说回来,NSURLSession是一个新课程,其目的无疑是为了减轻你的负担.

一句话:如果你想减少麻烦 - 但控制力较少 - 并且相信Apple会代表你胜任执行网络任务,请使用NSURLSession.否则,使用NSURLConnection和您自己的操作队列自行滚动.

  • 使用NSOperationQueue的一大好处是您可以在操作之间添加依赖项。IE在操作2之前先运行操作1。您不能使用NSURLSession进行操作,因为会话不会公开该队列。 (2认同)

yag*_*eek 6

更新:executingfinishing性质保存有关当前状态的知识NSOperation.一旦finishing被设定为YESexecutingNO,您的操作被视为完成.处理它的正确方法不需要a dispatch_group,可以简单地写成异步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]
   }
Run Code Online (Sandbox Code Playgroud)

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

如果isAsynchronous设置为YES,则表示代码的某些部分是以异步main方式执行的.换句话说:main方法内部进行异步调用,方法将在main方法完成后完成.

我有一些关于如何在apple os上处理并发的幻灯片:https://speakerdeck.com/yageek/concurrency-on-darwin.

老答案:你可以尝试一下dispatch_group_t.您可以将它们视为GCD的保留计数器.

想象一下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;
}
Run Code Online (Sandbox Code Playgroud)