如何进行多个GET请求并等待所有操作都完成?

时间:2014-08-06 07:31:19

标签: ios objective-c core-data afnetworking objective-c-blocks

对于我的应用,我需要从服务器加载不同的数据,因此我需要发出几个GET个请求。 对于每个GET请求,我需要将数据保存在我的CoreData中......所以我需要回调或每个操作的成功块。

我尝试了几件事,但我无法使它发挥作用。

当我执行单个GET请求时:

    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    [manager GET:[NSString stringWithFormat:@"%@/api/actors", [NSString stringWithUTF8String:kBaseURL]] parameters:nil 
success:^(AFHTTPRequestOperation *operation, id responseObject) {
  // Core Data saving
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

如何制作多个GET并等待我的所有请求都完成?

如果有人给我写了一个解决方案......它是否适用于其他类型的请求? (POST,DELETE,PUT ......等)

由于

6 个答案:

答案 0 :(得分:3)

dispatch_group可能是一种可能的解决方案,同时保持令人难以置信的通用结构,并且还可以处理您想要的任何类型的请求。

而不是重新创建一个好例子,commandshift has a good writeup explaining them and a great example of how to use them

答案 1 :(得分:2)

AFHTTPRequestOperation类是AFURLConnectionOperation的子类,它提供了方法

+ (NSArray *)batchOfRequestOperations:(NSArray *)operations progressBlock:(void ( ^ ) ( NSUInteger numberOfFinishedOperations , NSUInteger totalNumberOfOperations ))progressBlock completionBlock:(void ( ^ ) ( NSArray *operations ))completionBlock

此方法需要NSArray个操作和两个块,一个用于更新进度(如果需要),另一个用于完成时调用。此方法负责您必须手动完成的完成组/完成队列舞蹈。操作完成后,将调用完成块,您可以使用AFHTTPRequestOperationAFURLConnectionOperation的属性循环操作以获取响应数据并将其保存到Core Data对象。

您可以按如下方式创建操作:

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = // set the serializer if you need something other than JSON

如果您希望将响应对象存储在私有属性中,然后在完成块中访问它们,然后将它们保存到Core Data对象,也可以设置完成块属性。 。这将简化完成块中所需的逻辑。

为了让操作运行,您需要创建一个NSOperationQueue并添加操作。它将如下:

NSArray *operations = @[op1, op2, op3...];
NSOperationQueue *downloadQueue = [[NSOperationQueue alloc] init];
[downloadQueue addOperations:operations waitUntilFinished:NO];

答案 2 :(得分:1)

更新答案

在这种情况下,您可以编写一个更通用的方法,以递归方式遍历包含您的请求路径的数组:

// --------------------------------------------------------
// Header file
// --------------------------------------------------------
@property (nonatomic, strong) NSMutableArray *arrRequestURLs;

// --------------------------------------------------------
// Implementation file
// --------------------------------------------------------
-(void)viewDidLoad
{
    ...

    self.arrRequestURLs = [[NSMutableArray alloc] init];

    [self.arrRequestURLS addObject:@[@"http://api.myserver.com/api/actors"];
    [self.arrRequestURLS addObject:@[@"http://api.myserver.com/api/directors"];
    [self.arrRequestURLS addObject:@[@"http://api.myserver.com/api/photographers"];

    [self performRequestQueue:self.arrRequestURLS onSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

        // all request completed successfully do something here

    } onFailure: ^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Failed to complete all request");
    } onEachSuccess: ^(AFHTTPRequestOperation *operation, id *responseObject, NSString *sourceRequestURL) {

        // Scroll down to bottom see method implementation
        [self handleIntermediateSuccessOperation:operation 
                                      Response:responseObject 
                              ForRequestURL:sourceRequestURL];
    }];
}

// ------------------------------------------------------------------------------
// A recursive method that executes each request string in the passed in array
// and removes from array after successfully performing request, then calls itself
// again until arrRequestURLs is empty.
// ------------------------------------------------------------------------------
-(void)performRequestQueue:(NSMutableArray *)arrRequestURLs 
                 onSuccess:(void(^)(AFHTTPRequestOperation *operation, id responseObject))success 
                 onFailure:(void(^)(AFHTTPRequestOperation *operation, NSError *error))failure
                 onEachSuccess: (void(^)(AFHTTPRequestOperation *operation, id *responseObject, NSString *sourceRequestURL))intermediateSuccess
{
    if(arrRequestURLs.count > 0)
    {
        // make request
        [manager GET:arrRequestURLs[0] parameters:nil 
        success:^(AFHTTPRequestOperation *operation, id responseObject) {

           // call intermediate success block if any
           if(intermediateSuccess)
           {
               intermediateSuccess(operation, responseObject, arrRequestURLs[0]);
           }

           // remove the current str from array
           [arrRequestURLs removeObjectAtIndex:0];

            // check to see if there more request
            if(arrRequestURLs.count > 0)
            {
                // recursively call this method again to perform next URL string from array
                [self performRequestQueue:arrRequestURLs onSuccess:success onFailure:failure];
            }
            else
            {
                // no more request in arrRequestURLs, call finish block if any
                if(success)
                {
                    success(operation, responseObject);
                }
            }           
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            if(failure)
            {
                failure(operation, error);
            }
        }];
    }
}


// ------------------------------------------------------------------------------
// This method here handles the in-between success block for each operation
// including the last final success block.
// ------------------------------------------------------------------------------
-(void)handleIntermediateSuccessOperation:(AFHTTPRequestOperation *)operation 
                                          Response:(id)responseObject
                                  ForRequestURL:(NSString *)sourceRequestURL
{
    // ------------------------------------------------------------
    // Alternative, you can probably use a for loop here to get the
    // matching array index from self.arrRequestURLs and use it.
    // ------------------------------------------------------------
    if([sourceRequestURL isEqualToString:self.arrRequestURLs[0]])
    {
        // success block for actors, do something here
    }
    else if([sourceRequestURL isEqualToString:self.arrRequestURLs[1]])
    {
        // success block for directors, do something here
    }
    else
    {
        // success block for photographers, do something here
    }
}

原始答案

您可以链接您的请求:

[manager GET:[NSString stringWithFormat:@"%@/api/actors", [NSString stringWithUTF8String:kBaseURL]] parameters:nil 
success:^(AFHTTPRequestOperation *operation, id responseObject) {
  // Core Data saving

    [manager GET:[NSString stringWithFormat:@"%@/api/directors", [NSString stringWithUTF8String:kBaseURL]] parameters:nil 
    success:^(AFHTTPRequestOperation *operation, id responseObject) {
      // Core Data saving


        [manager GET:[NSString stringWithFormat:@"%@/api/photographers", [NSString stringWithUTF8String:kBaseURL]] parameters:nil 
        success:^(AFHTTPRequestOperation *operation, id responseObject) {
          // Core Data saving

            // ---------------------------------------------
            // Finished downloading all data
            // do something here 
            // ---------------------------------------------


        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            NSLog(@"Error: %@", error);
        }];


    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Error: %@", error);
    }];

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

答案 3 :(得分:0)

如果您只有少量请求,则可以在成功块中嵌套下一个操作。如果您的操作次数少于几次,我建议您使用NSOperationQueue。

答案 4 :(得分:0)

试试这个:

    NSInteger successCount;

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    [manager GET:someUrl parameters:nil 
success:^(AFHTTPRequestOperation *operation, id responseObject) {
  successCount++;
  if(successCount == 3){
     //do stuff
  }
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

[manager GET:someUrl parameters:nil 
success:^(AFHTTPRequestOperation *operation, id responseObject) {
  successCount++;
  if(successCount == 3){
     //do stuff
  }
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

[manager GET:someUrl parameters:nil 
success:^(AFHTTPRequestOperation *operation, id responseObject) {
  successCount++;
  if(successCount == 3){
     //do stuff
  }
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

答案 5 :(得分:0)

我找到了AFNetworking的一个很好的解决方案,我可以做我想做的事情:

NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:mutableOperations progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
        NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);
    } completionBlock:^(NSArray *operations) {
        [self.delegate dataRefreshingDone];
    }];
    [[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];

它将逐个执行我的所有GET操作。 我根据getter创建了一个特殊的url来创建一个操作。

- (AFHTTPRequestOperation *)operationGetForUrl:(NSString *)url
{
    NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/api/%@", [NSString stringWithUTF8String:kBaseURL], url]];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
    [request setHTTPMethod:@"GET"];
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    operation.responseSerializer = [AFJSONResponseSerializer serializer];
    [operation.responseSerializer setAcceptableContentTypes:    [operation.responseSerializer.acceptableContentTypes setByAddingObject:@"text/html"]];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
       // YOUR SUCCESS BLOCK
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
       // FAILURE BLOCK
    }];
    return operation;
}

正如你所看到的,对于每个操作,我都有一个成功的块,它可以做一些通用的东西......或者你可以制作具体的操作getter。