Как определить, завершены ли блоки в циклах?

У меня установлен цикл, который загружает серию изображений, которые позже я буду использовать для анимации, используя свойство animationImages UIImageView. Я хотел бы знать, когда все блоки внутри моих циклов закончили выполнение, чтобы я мог начать анимацию, и задавался вопросом, как я могу сказать, когда они закончат завершение? Спасибо!

for (PFObject *pictureObject in objects){

    PFFile *imageFile = [pictureObject objectForKey:@"image"];
    NSURL *imageFileURL = [[NSURL alloc] initWithString:imageFile.url];
    NSURLRequest *imageRequest = [NSURLRequest requestWithURL:imageFileURL];

    [tokenImageView setImageWithURLRequest:imageRequest placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
       [self.downloadedUIImages addObject:image]; //This is a mutableArray that will later be set to an UIImageView animnationImages

    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
        NSLog(@"Error %@", error);
    }];

}
//When I know all the blocks have finished downloading, I will then to animate the downloaded images. 

Изменить: проблема с Error -999

У меня возникает следующая проблема при выполнении кода в предоставленном ответе: Domain=NSURLErrorDomain Code=-999 "The operation couldn’t be completed. (NSURLErrorDomain error -999.)"

Быстрый поиск показывает, что Error -999 означает "другой запрос выполняется до того, как будет выполнен предыдущий запрос"... что, безусловно, имеет место здесь, так как я делая несколько запросов в быстрой последовательности. Рекомендуемое исправление, предложенное здесь, не работает для меня, так как оно будет успешно загружать только один UIImage (последний запрос), при этом предыдущие не работают. Мне было интересно, есть ли обходной путь здесь или в AFNetworking, что я должен рассмотреть? Спасибо!

Изменить 2: рабочий код на основе решения @David

for (PFObject *pictureObject in objects){

    PFFile *imageFile = [pictureObject objectForKey:@"image"];
    NSURL *imageFileURL = [[NSURL alloc] initWithString:imageFile.url];
    NSURLRequest *imageRequest = [NSURLRequest requestWithURL:imageFileURL];
    AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:imageRequest];
    requestOperation.responseSerializer = [AFImageResponseSerializer serializer];

    dispatch_group_enter(group);
    [requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"Response: %@", responseObject);
        UIImage *retrivedImage = (UIImage *)responseObject;
        [self.downloadedUIImages addObject:retrivedImage];

        dispatch_group_leave(group);

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

        dispatch_group_leave(group);
    }];

    [requestOperation start];
    counter ++;
}

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"Horray everything has completed");
    NSLog(@"What is here %@", self.downloadedUIImages);
    NSLog(@"Done");

});

Ответ 1

Создайте группу отправки, в цикле for введите группу, в блоке завершения покиньте группу. Затем вы можете использовать dispatch_group_notify, чтобы узнать, когда все блоки завершены:

dispatch_group_t    group = dispatch_group_create();

for (PFObject *pictureObject in objects){

    PFFile *imageFile = [pictureObject objectForKey:@"image"];
    NSURL *imageFileURL = [[NSURL alloc] initWithString:imageFile.url];
    NSURLRequest *imageRequest = [NSURLRequest requestWithURL:imageFileURL];

    dispatch_group_enter(group);
    [tokenImageView setImageWithURLRequest:imageRequest placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
        [self.downloadedUIImages addObject:image]; //This is a mutableArray that will later be set to an UIImageView animnationImages
        dispatch_group_leave(group);
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
        NSLog(@"Error %@", error);
        dispatch_group_leave(group);
    }];

}

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // do your completion stuff here
});

Ответ 2

Подсчитайте, сколько вы закончили. Сложная часть делает его потокобезопасным. Я рекомендую создать для этого класс атомного счетчика.

Общее решение!

+ (void)runBlocksInParallel:(NSArray *)blocks completion:(CompletionBlock)completion {
    AtomicCounter *completionCounter = [[AtomicCounter alloc] initWithValue:blocks.count];
    for (AsyncBlock block in blocks) {
        block(^{
            if ([completionCounter decrementAndGet] == 0) {
                if (completion) completion();
            }
        });
    }
    if (blocks.count == 0) {
        if (completion) completion();
    }
}

NSMutableArray *asyncBlocks = [NSMutableArray array];
for (PFObject *pictureObject in objects){
    [asyncBlocks addObject:^(CompletionBlock completion) {
        PFFile *imageFile = [pictureObject objectForKey:@"image"];
        NSURL *imageFileURL = [[NSURL alloc] initWithString:imageFile.url];
        NSURLRequest *imageRequest = [NSURLRequest requestWithURL:imageFileURL];

        [tokenImageView setImageWithURLRequest:imageRequest placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
           [self.downloadedUIImages addObject:image]; //This is a mutableArray that will later be set to an UIImageView animnationImages
        } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
            NSLog(@"Error %@", error);
        } completion:completion];
    }];
}
[BlockRunner runBlocksInParallel:[asyncBlocks copy] completion:^{
    //Do your final completion here!
}];

Ответ 3

Настройте свойство и инициализируйте его числом циклов - objects.count. По завершении блока опустите число вниз. Когда вы достигнете нуля, вы закончите.

Ответ 4

for (PFObject *pictureObject in objects){

    PFFile *imageFile = [pictureObject objectForKey:@"image"];
    NSURL *imageFileURL = [[NSURL alloc] initWithString:imageFile.url];
    NSURLRequest *imageRequest = [NSURLRequest requestWithURL:imageFileURL];

    [tokenImageView setImageWithURLRequest:imageRequest placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
       [self.downloadedUIImages addObject:image]; //This is a mutableArray that will later be set to an UIImageView animnationImages
       if([[objects lastObject] isEqual:pictureObject]) {
           [self animateImages];
       }
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
        NSLog(@"Error %@", error);
        if([[objects lastObject] isEqual:pictureObject]) {
           [self animateImages];
       }
    }];

}

- (void)animateImages {
    //do animation here.
}