AFNetworking 2 组合批次与依赖关系

AFNetworking 2 composing batch with dependencies

我正在使用 AFNetworking 2,并且想使用 NSURLSession 方法,但请阅读 GitHub issue where Mattt explains 为什么这不适用于批处理。因此,我使用的是来自包含 NSOperationQueue 的单例 class 的 AFHTTPRequestOperations。

我创建了大量的离散操作。这些操作中的每一个都是从应用程序的不同区域调用的,但在应用程序的某些部分,将它们批处理在一起很有用(想想 "full refresh")。这是执行此操作的方法:

-(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 添加到队列中。当然,这些额外的操作不计入 "batch"(如上所述),因此该批次在 opB 和 opC 完成之前完成。

问题1:从另一个完成块中添加一个操作时,我可以将它添加到"batch"(用于整体批量完成跟踪)吗?

我尝试过的一种替代方法是在批量创建时对队列中的所有操作进行排序(如下)。这提供了准确的批次完成通知。但是,由于 stu1B 需要来自 stu1A 的数据,而 stu1C 需要来自 stu1B 的数据,因此这仅在前置操作将其数据持久化到某个地方(例如 NSUserDefaults)且后续操作可以获取它的情况下才有效。这看起来有点"inelegant",但确实有效。

-(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:是否有更好的方法(除了在每个操作中持久化数据,然后在后续操作中从存储中读取数据)批量传递依赖操作之间的数据?

最后,我突然想到我可能让整个过程变得比应该的更困难。我很想听听仍然提供整体并发队列的替代方法,仍然提供整体批处理 progress/completion 跟踪,而且还允许操作间依赖性管理和数据传递。谢谢!

你不应该为此使用 NSOperation 依赖项,因为以后的操作依赖于 completionBlock 的处理,但 NSOperationQueue 认为这是一种副作用。

根据文档,completionBlock 是 "the block to execute after the operation’s main task is completed"。在 AFHTTPRequestOperation 的情况下, "the operation’s main task" 是 "making an HTTP request"。 "main task" 不包括 解析 JSON、持久化数据、检查 HTTP 状态代码等 - 这些都在 completionBlock 中处理。

所以在你的代码中,如果ssoA操作成功发起网络请求,但是认证失败,后面的所有操作仍然会继续。

相反,您应该只添加来自早期操作的完成块的依赖操作。

When adding an op from the completion block of another, can I add it to the "batch" (for overall batch completion tracking)?

你不能,因为:

  1. 此时构建批处理操作为时已晚(参见the implementation
  2. 这没有意义,因为以后的操作可能永远不会创建(例如,如果身份验证失败)

作为替代方案,您可以创建一个 NSProgress object,并随着工作的进展对其进行更新,以反映已完成的工作和已知将保留的工作。例如,您可以使用它来更新 UIProgressView.

Is there a better way (other than persisting data in each op and then reading from storage in the successor op) to pass data between dependent operations in a batch?

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