web-dev-qa-db-fra.com

Attendez que plusieurs demandes de mise en réseau aient toutes été exécutées, y compris leurs blocs d'achèvement

J'ai plusieurs opérations (ce sont des demandes AFNetworking) avec des blocs d'achèvement qui prennent un certain temps à exécuter et un objet Core Data qui doit être enregistré à la fin de toutes les demandes.

MyCoreDataObject *coreDataObject;

AFHTTPRequestOperation *operation1 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation1 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    coreDataObject.attribute1 = responseObject;
    sleep(5);
}];
[operation1 start];

AFHTTPRequestOperation *operation2 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation2 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    coreDataObject.attribute2 = responseObject;
    sleep(10);
}];
[operation1 operation2];

[context save:nil];

Bien sûr, cela ne fonctionne pas comme je le souhaite car les requêtes sont asynchrones. J'ai essayé d'ajouter un NSOperationQueue comme ceci:

NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue setMaxConcurrentOperationCount:2];

AFHTTPRequestOperation *operation1 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation1 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    coreDataObject.attribute1 = responseObject;
    sleep(5);
}];
[operationQueue addOperation:operation1];

AFHTTPRequestOperation *operation2 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation2 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    coreDataObject.attribute2 = responseObject;
    sleep(10);
}];
[operationQueue addOperation:operation2];

[imageQueue waitUntilAllOperationsAreFinished];
[context save:nil];

Cela semble un peu mieux. En utilisant waitUntilAllOperationsAreFinished, ma file d'attente bloque le thread actuel jusqu'à ce que mes demandes soient terminées, mais pas jusqu'à ce que mes blocs de réussite soient terminés, ce dont j'ai vraiment besoin.

Avez-vous des idées sur la façon d'y parvenir dans le bon sens?

39
choise

Utilisez des groupes de répartition.

dispatch_group_t group = dispatch_group_create();

MyCoreDataObject *coreDataObject;

dispatch_group_enter(group);
AFHTTPRequestOperation *operation1 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation1 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    coreDataObject.attribute1 = responseObject;
    sleep(5);
    dispatch_group_leave(group);
}];
[operation1 start];

dispatch_group_enter(group);
AFHTTPRequestOperation *operation2 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation2 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    coreDataObject.attribute2 = responseObject;
    sleep(10);
    dispatch_group_leave(group);
}];
[operation2 start];

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);

[context save:nil];
84
Ken Thomases

AFNetworking a conçu une méthode pour ce type d'opérations, qui résume GCD:

-enqueueBatchOfHTTPRequestOperationsWithRequests:progressBlock:completionBlock:

-enqueueBatchOfHTTPRequestOperations:progressBlock:completionBlock:

Jetez un œil à FAQ

15
wzs

Je crois quelque chose comme ça:

NSMutableArray *mutableOperations = [NSMutableArray array];
for (NSURL *fileURL in filesToUpload) {
    NSURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
        [formData appendPartWithFileURL:fileURL name:@"images[]" error:nil];
    }];

    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

    [mutableOperations addObject:operation];
}

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

se référant aux documents: http://cocoadocs.org/docsets/AFNetworking/2.5.0/

1
yershuachu

Mes exigences étaient celles de faire beaucoup de demandes à partir d'un tableau de chaînes (URL)

func updateSourceData(element: Int) {

    if element > availableUrls.count - 1 {
        return
    }

    let service = SourceDataServiceDao()
    let currentUrl = availableUrls[element]
    service.fooCall(url: currentUrl, completion: { (response, error) -> Void in
        self.updateSourceData(element + 1)
    })

}

De toute évidence, de cette façon, les appels sont effectués en cascade, pas N appels asynchrones.

1
Luca Davanzo