Подождите, пока блоки assetsForURL будут завершены.

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

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)
    }];
}

person Mathieu    schedule 29.08.2011    source источник


Ответы (5)


Подход семафора 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);
person Chad Podoski    schedule 18.05.2012
comment
обратите внимание, что если этот код не должен вызываться в фоновом потоке (например, с использованием performSelectorInBackground:), так как произойдет взаимоблокировка (кажется, что блоки вызываются в одном потоке, поэтому семафор никогда не сигнализируется). - person alex-i; 26.09.2013
comment
Это должен быть выбранный ответ @Mathieu - person brandonscript; 16.04.2015

Решение находится здесь http://omegadelta.net/2011/05/10/how-to-wait-for-ios-methods-with-completion-blocks-to-finish/

person Mathieu    schedule 05.09.2011
comment
Это ответ только для ссылки, и его следует улучшить, включив соответствующий код из исходной статьи и модернизировав (поскольку этому ответу уже 4 года). - person brandonscript; 15.04.2015

Обратите внимание, что assetsForURL:resultBlock:failureBlock: зависнет, если основной поток ожидает без запуска RunLoop. Это альтернативное (более чистое :-)) решение:

#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]];
}
person Johnny Wong    schedule 14.06.2012
comment
Обнаружил, что мой метод не всегда работает (может случиться бесконечное ожидание). Я предлагаю метод семафора GCD от shacked, который работает для меня. - person Johnny Wong; 05.07.2012

Проще всего переместить код внутрь (в конец) resultBlock или failureBlock. Таким образом, ваш код будет выполняться в правильном порядке, и вы также сохраните асинхронное поведение.

person Chaitanya Gupta    schedule 29.08.2011
comment
Вы имеете в виду после else } Дело в том, что мне нужно, чтобы цикл был завершен, а также код, который следует, представляет собой большой фрагмент кода с другими асинхронными функциями... - person Mathieu; 30.08.2011
comment
Предполагая, что это выполняется в основном потоке, вы действительно хотите заблокировать весь основной поток, ожидая завершения этого блока? - person Chaitanya Gupta; 30.08.2011

Это простой способ сделать это. Может быть, не так элегантно, как использование GCD, но он должен выполнить свою работу... Это сделает ваш метод блокирующим, а не неблокирующим.

__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]];
}
person Adam Freeman    schedule 19.06.2014