2011-08-29 2 views
24

계속 진행하기 위해이 코드가 실행되기를 기다리고 싶습니다. 그러나이 블록을 assynchronously라고 불렀으므로 어떻게 해야할지 모르겠습니다. ??? 할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

이것은 선택된 대답이어야합니다. @Mathieu – brandonscript

4

참고 assetForURL : 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]]; 
} 
+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]]; 
}