2011-08-29 5 views
24

私はこのコードを実行する前に実行を待つことを望みますが、これらのブロックは非同期で呼び出されるため、どのように実行するのかわかりません。assetForURLブロックが完了するまで待つ

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

答えて

1

一番簡単な方法は、(の終わりに)内側にresultBlockまたはfailureBlockをあなたのコードを移動することです。これにより、コードが正しい順序で実行され、非同期動作も保持されます。

+0

あなたは 事は、私はループが終了したとしても、次のされているコードは、他の非同期機能とコードのAA大部分である必要である}他の後に意味... – Mathieu

+0

これを実行していると仮定すると、メインスレッド、このブロックが終了するのを待っている間にメインスレッド全体を本当にブロックしたいのですか? –

+0

これはperformselectorinbackgroundでこれを呼び出すためです。 – Mathieu

44

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); 
+4

このコードをバックグラウンドスレッドで呼び出すべきではない場合(例: 'performSelectorInBackground:'を使用して)、デッドロックが発生する(ブロックは同じスレッド上で呼び出されるようですが、セマフォは決して通知されません)。 –

+1

これは選択された答えにする必要があります@マチュー – brandonscript

4

なおassetForURL:resultBlock:failureBlock:メインスレッドが実行ループを実行せずに待機している場合立ち往生します。これは代替(クリーナー:-))ソリューションです:

#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]]; 
} 
+1

私の方法は常に仕事ではないことがわかりました(無限の待機が発生する可能性があります)。私はshackedのGCDセマフォーメソッドを提案してくれます。 –

1

これは簡単な方法です。おそらく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]]; 
} 
関連する問題