notes apple app iphone objective-c alasset alassetslibrary

iphone - apple - Espere a que los bloques de assetForURL se completen



icloud (5)

Me gustaría esperar a que este código se ejecute antes de continuar, pero como estos bloques se llaman de forma asíncrona, no sé cómo hacerlo.

NSURL *asseturl; NSMutableArray *tmpListAsset = [[NSMutableArray alloc] init]; ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease]; NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init]; for (NSDictionary *dico in assetsList) { asseturl = [NSURL URLWithString:[dico objectForKey:@"assetUrl"]]; NSLog(@"asset url %@", asseturl); // Try to load asset at mediaURL [library assetForURL:asseturl resultBlock:^(ALAsset *asset) { // If asset doesn''t exists if (!asset){ [objectsToRemove addObject:dico]; }else{ [tmpListAsset addObject:[asseturl absoluteString]]; NSLog(@"tmpListAsset : %@", tmpListAsset); } } failureBlock:^(NSError *error) { // Type your code here for failure (when user doesn''t allow location in your app) }]; }


Enfoque del semáforo GCD:

dispatch_semaphore_t sema = dispatch_semaphore_create(0); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0); for (NSURL *url in self.assetUrls) { dispatch_async(queue, ^{ [library assetForURL:url resultBlock:^(ALAsset *asset) { [self.assets addObject:asset]; dispatch_semaphore_signal(sema); } failureBlock:^(NSError *error) { dispatch_semaphore_signal(sema); }]; }); dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); } dispatch_release(sema); /* Check out ALAssets */ NSLog(@"%@", self.assets);


Esta es una manera fácil de hacerlo. Tal vez no sea tan elegante como usar GCD pero debería hacer el trabajo ... Esto hará que su método se bloquee en lugar de no bloquearse.

__block BOOL isFinished = NO; NSURL *asseturl; NSMutableArray *tmpListAsset = [[NSMutableArray alloc] init]; ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init]; NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init]; for (NSDictionary *dico in assetsList) { asseturl = [NSURL URLWithString:[dico objectForKey:@"assetUrl"]]; NSLog(@"asset url %@", asseturl); // Try to load asset at mediaURL [library assetForURL:asseturl resultBlock:^(ALAsset *asset) { // If asset doesn''t exists if (!asset){ [objectsToRemove addObject:dico]; }else{ [tmpListAsset addObject:[asseturl absoluteString]]; NSLog(@"tmpListAsset : %@", tmpListAsset); } if (objectsToRemove.count + tmpListAsset.count == assetsList.count) { isFinished = YES; } } failureBlock:^(NSError *error) { // Type your code here for failure (when user doesn''t allow location in your app) isFinished = YES; }]; } while (!isFinished) { [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01f]]; }



Lo más fácil de hacer es mover su código al interior (al final de) el resultBlock o el failureBlock . De esa manera, su código se ejecutará en el orden correcto y también conservará el comportamiento asíncrono.


Tenga en cuenta que assetForURL: resultBlock: failureBlock: se atascará si el hilo principal está esperando sin que se ejecute RunLoop. Esta es una solución alternativa (limpiador :-)):

#import <libkern/OSAtomic.h> ... ALAssetsLibrary *library; NSMutableArray *assets; ... __block int32_t counter = 0; for (NSURL *url in urls) { OSAtomicIncrement32(&counter); [library assetForURL:url resultBlock:^(ALAsset *asset) { if (asset) [assets addObject:asset]; OSAtomicDecrement32(&counter); } failureBlock:^(NSError *error) { OSAtomicDecrement32(&counter); }]; } while (counter > 0) { [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; }