AFNetworking 2编写具有依赖关系的批处理

时间:2015-07-12 16:39:26

标签: ios afnetworking-2 nsoperationqueue

我正在使用AFNetworking 2,并希望使用NSURLSession方法,但请阅读GitHub issue where Mattt explains为什么这不适用于批处理。所以,相反,我正在使用来自包含NSOperationQueue的单例类的AFHTTPRequestOperations。

我创造了大量的离散操作。这些操作中的每一个都是从应用程序的不同区域调用的,但在应用程序的某些部分中,将它们一起批处理(将其视为“完全刷新”)非常有用。这是一种方法:

-(void) getEverything {

  AFHTTPRequestOperation *ssoA = [SecurityOps authenticateSSO];

  AFHTTPRequestOperation *atSC = [SecurityOps attachSessionCookies];
  [atSC addDependency:ssoA];

  AFHTTPRequestOperation *comL = [CommunityOps communityListOp];
  [comL addDependency:ssoA];

  AFHTTPRequestOperation *comS = [CommunityOps searchCommunityOp:nil :nil];
  [comS addDependency:comL];

  AFHTTPRequestOperation *stu1 = [StudentOps fdpFullOp]; // 3 Ops in Sequence
  [stu1 addDependency:ssoA];

  AFHTTPRequestOperation *stu2 = [StudentOps progressDataOp];
  [stu2 addDependency:ssoA];

  AFHTTPRequestOperation *stu3 = [StudentOps programTitleOp];
  [stu3 addDependency:ssoA];

  AFHTTPRequestOperation *stu4 = [StudentOps graduationDateOp];
  [stu4 addDependency:ssoA];

  NSArray *ops = [AFURLConnectionOperation
                  batchOfRequestOperations:@[ssoA, atSC, comL, comS, stu1, stu2, stu3, stu4]
                             progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {

    NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);

  } completionBlock:^(NSArray *operations) {

    NSLog(@"All operations in batch complete");

  }];

  [self.Que addOperations:ops waitUntilFinished:NO];
}

这很好用,但有一个例外:“fdpFullOp”实际上是按顺序启动其他操作。在其完成块中,它将opB添加到队列中,然后opB将opC添加到其完成块中的队列中。当然,这些附加操作不计入“批处理”(如上所述),因此该批处理在opB和opC完成之前完成。

问题1:当从另一个的完成块添加操作时,我可以将其添加到“批处理”(用于整体批量完成跟踪)吗?

我尝试过的另一种方法是在批量创建时对队列中的所有操作进行排序(下图)。这提供了准确的批量完成通知。但是,由于stu1B需要来自stu1A的数据,并且stu1C需要来自stu1B的数据,所以只有在前任操作将其数据保留在某个地方(例如NSUserDefaults)后继操作才能获得数据时,这才有效。这似乎有点“不优雅”,但确实有效。

-(void) getEverything {

  AFHTTPRequestOperation *ssoA = [SecurityOps authenticateSSO];

  AFHTTPRequestOperation *atSC = [SecurityOps attachSessionCookies];
  [atSC addDependency:ssoA];

  AFHTTPRequestOperation *comL = [CommunityOps communityListOp];
  [comL addDependency:ssoA];

  AFHTTPRequestOperation *comS = [CommunityOps searchCommunityOp:nil :nil];
  [comS addDependency:comL];

  AFHTTPRequestOperation *stu1A = [StudentOps fdpFullOp]; // 1 of 3 op sequence
  [stu1A addDependency:ssoA];

  AFHTTPRequestOperation *stu1B = [StudentOps fdpSessionOp]; // 2 of 3 op sequence
  [stu1B addDependency:stu1A];

  AFHTTPRequestOperation *stu1C = [StudentOps fdpDegreePlanOp]; // 3 of 3 op sequence
  [stu1C addDependency:stu1B];

  AFHTTPRequestOperation *stu2 = [StudentOps progressDataOp];
  [stu2 addDependency:ssoA];

  AFHTTPRequestOperation *stu3 = [StudentOps programTitleOp];
  [stu3 addDependency:ssoA];

  AFHTTPRequestOperation *stu4 = [StudentOps graduationDateOp];
  [stu4 addDependency:ssoA];

  NSArray *ops = [AFURLConnectionOperation
                  batchOfRequestOperations:@[ssoA, atSC, comL, comS, stu1A, stu1B, stu1C, stu2, stu3, stu4]
                             progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {

    NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);

  } completionBlock:^(NSArray *operations) {

    NSLog(@"All operations in batch complete");

  }];

  [self.Que addOperations:ops waitUntilFinished:NO];
}

问题2:是否有更好的方法(除了在每个操作数中保留数据,然后从后续操作中的存储中读取数据)在批处理中的相关操作之间传递数据?

最后,我发现我可能会让整个过程变得更加困难。我很想知道仍然提供整体并发队列的备用方法,仍提供整体批处理进度/完成跟踪,但也允许操作间依赖关系管理和数据传递。谢谢!

1 个答案:

答案 0 :(得分:1)

您不应该使用NSOperation依赖项,因为以后的操作依赖于completionBlock的处理,但NSOperationQueue认为这会产生副作用。

根据文档,completionBlock是“在操作的主要任务完成后执行的块”。在AFHTTPRequestOperation的情况下,“操作的主要任务”是“发出HTTP请求”。 “主要任务”不包括解析JSON,持久化数据,检查HTTP状态代码等等 - 这些都在completionBlock处理。

因此,在您的代码中,如果ssoA操作成功发出网络请求,但身份验证失败,则所有后续操作仍将继续。

相反,您应该只添加早期操作的完成块中的相关操作。

  

从另一个的完成块添加操作时,我可以将其添加到“批处理”(用于整体批量完成跟踪)吗?

你不能,因为:

  1. 此时构建批处理操作为时已晚(参见the implementation
  2. 没有意义,因为以后的操作可能永远不会被创建(例如,如果验证失败)
  3. 作为替代方案,您可以创建一个NSProgress object,并在工作进展时对其进行更新,以反映已完成的工作和已知的内容。例如,您可以使用它来更新UIProgressView

      

    是否有更好的方法(除了在每个操作数中保留数据,然后从后续操作中的存储中读取数据)在批处理中的相关操作之间传递数据?

    如果您从早期操作的完成块添加相关操作,那么您可以在验证成功条件后传递局部变量。