Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

wait for completion block before executing something

I am new to blocks and am trying to figure out how to wait for the block to finish before performing my action (in this case a nslog) So how can I wait till the block is done before performing this nslog in the code below: NSLog(@"convertedPhotos::%@",convertedImages);

          convertedImages =  [[NSMutableArray alloc] init];
        for (NSDictionary *photo in photos) {
            // photo is a dictionary containing a "caption" and a "urlRep"
            [photoUrls addObject:photo[@"urlRep"]];
        }

        if (photoUrls.count) {
            for (id photos in photoUrls){
                NSString *urlString = photos;
                [self base64ImageAtUrlString:urlString result:^(NSString *base64) {


                    [jsonWithPhotos setObject:convertedImages forKey:@"photo64"];
                    NSError *error;
                    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonWithPhotos
                                                                       options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
                                                                         error:&error];

                    if (! jsonData) {
                        NSLog(@"Got an error: %@", error);
                    } else {
                        NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
                        NSLog(@"json::%@",jsonString);

                    }
                }];
            }
        }
        else {
            NSLog(@"where are my urls?");
        }
        NSLog(@"convertedPhotos::%@",convertedImages);

    }

}

this method/block is called from above

- (void)base64ImageAtUrlString:(NSString *)urlString result:(void (^)(NSString *))completion {
    NSURL *url = [NSURL URLWithString:urlString];
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

    [library assetForURL:url resultBlock:^(ALAsset *asset) {

        // borrowing your code, here... didn't check it....
        ALAssetRepresentation *representation = [asset defaultRepresentation];
        CGImageRef imageRef = [representation fullResolutionImage];

        //TODO: Deal with JPG or PNG
        NSData *imageData = UIImageJPEGRepresentation([UIImage imageWithCGImage:imageRef], 0.1);
        NSString *base64 = [imageData base64EncodedString];
        completion(base64);
        [convertedImages addObject:base64];

//        NSLog(@"converted::%@",convertedImages);

    } failureBlock:^(NSError *error) {
        NSLog(@"that didn't work %@", error);
    }];
}
like image 820
BluGeni Avatar asked Feb 22 '26 20:02

BluGeni


2 Answers

I would use NSOperationQueue (or dispatch queue) and NSCondition (or dispatch group) to wait for operations to complete. It is also important to wrap blocks in @autoreleasepool to flush memory once you do not need it if you work with memory consuming objects like NSData.

example:

// create results array
__block NSMutableArray* results = [NSMutableArray new];

// create serial queue
dispatch_queue_t queue = dispatch_queue_create("myQueue", 0);

for(NSInteger i = 0; i < 10; i++) {
    // enqueue operation in queue
    dispatch_async(queue, ^{
        // create semaphore
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);

        // do something async, I do use another dispatch_queue for example
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
            // wrap in autoreleasepool to release memory upon completion
            // in your case wrap the resultBlock in autoreleasepool
            @autoreleasepool {
                // here for example the nested operation sleeps for two seconds
                sleep(2);

                // add the operation result to array
                // I construct an array of strings for example
                [results addObject:[NSString stringWithFormat:@"Operation %d has finished.", i]];

                // signal that nested async operation completed
                // to wake up dispatch_semaphore_wait below
                dispatch_semaphore_signal(sema);
            }
        });

        // wait until the nested async operation signals that its finished
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

        NSLog(@"Finished single operation.");
    });
}

// will be called once all operations complete
dispatch_async(queue, ^{
    NSLog(@"Finished all jobs.");
    NSLog(@"Results: %@", results);
});
like image 183
Rob Zombie Avatar answered Feb 24 '26 10:02

Rob Zombie


For any non-main queue use semaphores

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // some serious stuff here
    ...        
    dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

In case you want to wait for async task execution being in the main queue - you wouldn't probably want to block it while waiting for a semaphore. I use this construction which doesn't freeze the UI for the main queue only.

__block BOOL flag = NO;
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // some serious stuff here
    ...        
    flag = YES;
});
// Run until 'flag' is not flagged (wait for the completion block to finish executing
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) && !flag){};
like image 21
Vladimir Afinello Avatar answered Feb 24 '26 09:02

Vladimir Afinello