2014-09-29 3 views
1

Я хочу, чтобы обернуть SDK асинхронной API для синхронизации, код выглядит следующим образом:подождать асинхронный обратный вызов для синхронизации

dispatch_semaphore_t sema = dispatch_semaphore_create(0); 
__block BOOL _isLogined; 
__block BOOL _isCallback = NO; 
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
dispatch_async(queue,^{ 
    //Put your heavy code here it will not block the user interface 
    [[SDKPlatform defaultPlatform] SDKIsLogined:^(BOOL isLogined){ 
     _isLogined = isLogined; 
     _isCallback = YES; 
     dispatch_semaphore_signal(sema); 
    }]; 
}); 
while (!_isCallback) { 
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); 
} 
return _isLogined; 

я уже читал этот вопрос так же How do I wait for an asynchronously dispatched block to finish?

, но, когда он был вызова в потоке пользовательского интерфейса происходит тупиковая ситуация, так как обратный вызов SDK также запускает блок в потоке пользовательского интерфейса.

Как это сделать? Благодарю.

+0

, если я не понимаю что-то подход с семафорами и так далее совершенно неправильно, Эрик. Все вы делаете (1) запустить код и (2), когда закончите, оповестить основной поток. После этого вы можете сделать Следующее. Это абсолютно обычное дело, пример ниже! – Fattie

+0

Вам нужно увеличить количество семафоров на «1» (вместо этого используйте «1» 0), когда он создан. Создание семафора, не имеющего места для доступа к общему ресурсу, вызовет тупик при вызове dispatch_semaphore_wait. – user298261

ответ

0

Если SDKPlatform отправляет свой блок завершения обратно в основную очередь, то ваш подход блокировки основного потока до тех пор, пока не будет вызван блок завершения, наверняка затормозит, и вы вряд ли сможете это сделать. Но этот семафорный подход, чтобы блокировать основной поток, чтобы вы могли сделать асинхронный метод, как синхронный, во всяком случае, нецелесообразным. Фактически вы должны использовать асинхронные шаблоны и использовать методы блока завершения в своем собственном коде.

Эта ссылка, How do I wait for an asynchronously dispatched block to finish?, иллюстрирует использование семафоров для синхронной работы асинхронной задачи. К сожалению, эта техника неправильно используется с тревожной частотой. В частности, в этом случае семафор не является подходящим шаблоном в вашем сценарии, потому что шаблон семафора блокирует основной поток, что мы никогда не должны делать в приложении.

В качестве фона техника семафора отлично подходит для сценария, обсуждаемого в этом другом потоке, потому что это совсем другая техническая проблема. Он используется в особой ситуации в рамках тестирования, а не в приложении, и в ситуации, когда (а) сам тест должен произойти в основном потоке; (б) для функционирования структуры тестирования она должна блокировать основной поток до завершения асинхронной задачи. Кроме того, он также работает в этом сценарии тестирования, поскольку блок завершения не выполняется в основной очереди, избегая проблемы с тупиковой ситуацией, с которой вы столкнулись.

Ни одно из этих трех условий не имеет места в вашем случае. Использовать семафорную технику в вашей ситуации нецелесообразно.

Итак, давайте вернемся и рассмотрим вашу проблему. Я предполагаю, что у вас есть какой-то метод, который выглядит как:

- (BOOL) login 
{ 
    dispatch_semaphore_t sema = dispatch_semaphore_create(0); 
    __block BOOL _isLogined; 
    __block BOOL _isCallback = NO; 
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    dispatch_async(queue,^{ 
     //Put your heavy code here it will not block the user interface 
     [[SDKPlatform defaultPlatform] SDKIsLogined:^(BOOL isLogined){ 
      _isLogined = isLogined; 
      _isCallback = YES; 
      dispatch_semaphore_signal(sema); 
     }]; 
    }); 
    while (!_isCallback) { 
     dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); 
    } 
    return _isLogined; 
} 

Даже если вы не имеете проблему взаимоблокировки, это еще не тот шаблон. То, что вы, вероятно, хотите что-то вроде:

- (void)loginWithCompletionHandler:(void (^)(BOOL isLoggedIn))completionHandler 
{ 
    [[SDKPlatform defaultPlatform] SDKIsLogined:^(BOOL isLoggedIn){ 
     if (completionHandler) { 
      completionHandler(isLoggedIn); 
     } 
    }]; 
} 

Примечание, эта функция имеет тип void возврата, а скорее isLoggedIn состояние возвращается в блок завершения (и должен использоваться только в пределах блока завершения, например, так:

[self loginWithCompletionHandler:^(BOOL isLoggedIn) { 
    // feel free to use isLoggedIn here 
}]; 
// don't try to use isLoggedIn here 
Смежные вопросы