如何使用AFNetworking 2进行批量请求?

49
我正在使用AFNetworking 2.0重新编写一个iOS 7应用程序,遇到了一些问题:如何一次发送一批请求并跟踪它们的进度。在旧的AFNetworking中,AFHTTPClient上有enqueueBatchOfHTTPRequestOperations:progressBlock:completionBlock:方法,但该方法已被重构掉了。我创建了AFHTTPSessionManager的子类,并使用POST:...和GET:...方法与服务器通信,但我找不到任何可以同时排队多个请求的代码和/或文档,就像旧的AFHTTPClient一样。唯一能找到的是AFURLConnectionOperation上未记录的batchOfRequestOperations:progressBlock:completionBlock:方法,但这看起来像是iOS 6的做法。显然,我在新的NSURLSession概念中缺少某些东西,应该使用它来批处理请求,或者是忽略了新的AFNetworking功能。希望有人能帮我找出正确的方向!简而言之,我应该如何用我的AFHTTPSessionManager子类发送一批请求?

AFHTTPSessionManager 实现了 AFURLSessionManager,后者实现了四个重要的 delegate。包括 NSURLSessionDelegate、NSURLSessionTaskDelegate、NSURLSessionDataDelegate 和 NSURLSessionDownloadDelegate。其中,NSURLSessionDataDelegate 是 GET/POST 操作最常用的 delegate。由于我们使用单例模式创建了 sessionManager,因此您只需要覆盖这些方法即可。例如,完成任务的回调之一是 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask ... completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler。 - Pradeep Mahdevu
5个回答

85

感谢Sendoa提供的链接,链接指向GitHub问题页面,Mattt在其中解释了为什么这个功能不再起作用。使用新的NSURLSession结构是不可能实现这个功能的,因为任务只是操作的一部分,所以旧的依赖关系或批处理操作的方法将不再起作用。

我使用dispatch_group创建了这个解决方案,使得可以使用NSURLSession批量请求,以下是(伪)代码:

// Create a dispatch group
dispatch_group_t group = dispatch_group_create();

for (int i = 0; i < 10; i++) {
    // Enter the group for each request we create
    dispatch_group_enter(group);

    // Fire the request
    [self GET:@"endpoint.json"
       parameters:nil
          success:^(NSURLSessionDataTask *task, id responseObject) {
                  // Leave the group as soon as the request succeeded
                  dispatch_group_leave(group);
          }
      failure:^(NSURLSessionDataTask *task, NSError *error) {
                  // Leave the group as soon as the request failed
                  dispatch_group_leave(group);
              }];
}

// Here we wait for all the requests to finish
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // Do whatever you need to do when all requests are finished
});

我希望写一些东西,使得这件事更容易做和与马特讨论,看看是否可以将其合并到AFNetworking中(如果实现得好的话)。在我看来,使用这个库本身来做类似的事情是很棒的,但我必须等我有空闲时间才能进行检查。


如果第一次迭代在第二次迭代进入之前离开了该组怎么办? - Chrizzor
这不是问题。在这种情况下,for循环仍然会阻塞线程。因此,它将把所有其他请求附加到组中,只有在for循环完成后才会调用dispatch_group_notify(),一旦所有“迭代”离开组,该块就会被调用。 - Mac_Cain13
1
我对 objc/afnetworking 有点陌生。这里的“self”对象是什么?它不是 AFHTTPRequestOperationManager - zengr
如果我没错的话,ARC将处理组的释放/保留。 - Mac_Cain13
你会如何使请求并发?即使用(https://developer.apple.com/library/ios/documentation/General/Conceptal/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW5)- 并发调度队列。 - Ríomhaire
显示剩余2条评论

3

对于可以是postgetrequest,您可以使用AFNetworking 2.0进行批处理操作,首先需要创建如下操作:

//Request 1
NSString *strURL = [NSString stringWithFormat:@"your url here"];
NSLog(@"scheduleurl : %@",strURL);
NSDictionary *dictParameters = your parameters here
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:strURL parameters:dictParameters error: nil];

AFHTTPRequestOperation *operationOne = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operationOne = [AFHTTPResponseSerializer serializer];

[operationOne setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
     //do something on completion
} 
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
     NSLog(@"%@",[error description]);
}];

//Request 2
NSString *strURL1 = [NSString stringWithFormat:@"your url here"];
NSLog(@"scheduleurl : %@",strURL);
NSDictionary *dictParameters1 = your parameters here
NSMutableURLRequest *request1 = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:strURL1 parameters:dictParameters1 error: nil];

AFHTTPRequestOperation *operationTwo = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
operationTwo = [AFHTTPResponseSerializer serializer];

[operationTwo setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
     //do something on completion
} 
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
     NSLog(@"%@",[error description]);
}];

//Request more here if any

现在可以像这样执行批处理操作:
//Batch operation
//Add all operation here 
NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:@[operationOne,operationTwo] progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations)
{
    NSLog(@"%i of %i complete",numberOfFinishedOperations,totalNumberOfOperations);
    //set progress here
    yourProgressView.progress = (float)numberOfFinishedOperations/(float)totalNumberOfOperations;

} completionBlock:^(NSArray *operations) 
{
    NSLog(@"All operations in batch complete");
}];

[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];

3

仅更新线程... 我遇到了同样的问题,经过一些研究,我找到了一些好的解决方案,但我决定坚持这个:

我正在使用名为Bolts的项目。 因此,对于与@Mac_Cain13发布的相同示例,它应该是:

[[BFTask taskWithResult:nil] continueWithBlock:^id(BFTask *task) {
    BFTask *task = [BFTask taskWithResult:nil];
    for (int i = 0; i < 10; i++) {
        task = [task continueWithBlock:^id(BFTask *task) {
            return [self executeEndPointAsync];
        }];
    }
    return task;
}] continueWithBlock:^id(BFTask *task) {
    // Everything was executed.
    return nil;
}];;

- (BFTask *) executeEndPointAsync {
    BFTaskCompletionSource *task = [BFTaskCompletionSource taskCompletionSource];
    [self GET:@"endpoint.json" parameters:nil
      success:^(NSURLSessionDataTask *task, id responseObject) {
        [task setResult:responseObject];
      }
      failure:^(NSURLSessionDataTask *task, NSError *error) {
        [task setError:error];
      }];
    }];
    return task.task;
}

基本上,它会将所有任务堆叠起来,等待和解开,直到没有更多任务为止,在完成所有任务后执行最后一个完成块。

另一个执行相同操作的项目是RXPromise,但对我来说,Bolts中的代码更加清晰。 Bolts


1
在AFNetworking 2.0中,AFHTTPClient已经被拆分为AFHTTPRequestOperationManagerAFHTTPSessionManager,因此您可以从第一个开始,它具有operationQueue属性。

1

目前,NSURLSession 任务不适合使用与请求操作相同的模式。关于这个问题,请参见Mattt Thompson的回答这里

直接的回答是:如果你需要依赖项或批处理,仍然需要使用请求操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接