Cocoa синхронизация потоков при использовании [ALAssetsLibrary enumerateGroupsWithTypes:]

Недавно я, как и несколько человек, обнаружил, что [ALAssetsLibrary enumerateGroupsWithTypes] любит запускать свои блоки в другом потоке. Какой позор, что Apple не документировала это: -)

В моих текущих обстоятельствах мне нужно дождаться завершения перечисления, прежде чем основной поток вернет любые результаты. Мне явно нужна какая-то синхронизация потоков.

Я читал о NSLock и NSConditionLock, но пока ничего не похоже на требование "сигнал заблокированного потока, который завершил этот рабочий поток". Это кажется достаточно простой потребностью - может ли кто-нибудь указать мне в правильном направлении?

Ваша подсказка и боос, как всегда приветствуются,

М.

Ответ 1

Ответ заключается в том, чтобы использовать класс NSConditionLock таким образом...

typedef enum {
    completed = 0,
    running = 1
} threadState;

...

NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:running];

Затем открутите свой поток или в моем случае вызовите [ALAssetsLibrary enumerateGroupsWithTypes:]. Затем заблокируйте родительский поток этим...

// Await completion of the worker threads 
[lock lockWhenCondition:completed];
[lock unlockWithCondition:completed];

Когда вся работа выполняется в потоке child/worker, разблокируйте родителя с этим...

// Signal the waiting thread
[lock lockWhenCondition:running];
[lock unlockWithCondition:completed];

Ответ 2

Фреймворк не запускает эти блоки в отдельном потоке. Он просто запускает их как дополнительные события в одном и том же цикле. Чтобы доказать это, попробуйте это

    [library enumerateGroupsWithTypes:ALAssetsGroupAll 
                           usingBlock:[^(ALAssetsGroup * group, BOOL * stop)
                             {
                               if([NSThread isMainThread])
                               {
                                  NSLog(@"main");
                               }
                               else
                               {
                                 NSLog(@"non-main");
                               }
                             } copy] 
           failureBlock:^(NSError * err)
                          {NSLog(@"Erorr: %@", [err localizedDescription] );}];
    [library release];
    if([NSThread isMainThread])
    {
        NSLog(@"main");
    }
    else
    {
        NSLog(@"non-main");
    }

Мой результат из этого был

main
main
main

Значение того, что блок вызывается в основном потоке. Это просто отдельное событие. Чтобы решить вашу проблему, вам нужно как можно скорее вернуть свое значение изнутри блока, когда вы достигнете последнего шага. Вы можете сказать ему последний шаг, потому что ваш блок будет вызываться с nil для объекта group.

EDIT:, например, используйте этот блок

^(ALAssetsGroup * group, BOOL * stop)
{
    if(group == nil)
    {
        // we've enumerated all the groups 
        // do something to return a value somehow (maybe send a selector to a delegate)
    }
}

Ответ 3

Просто используйте это:

[library enumerateGroupsWithTypes:ALAssetsGroupAll 
                           usingBlock:[^(ALAssetsGroup * group, BOOL * stop)
{
    if(group == nil)
    {
        // this is end of enumeration
    }
}
.
.
.