Как обернуть асинхронный метод, который принимает блок и превратить его синхронно в объективе c
Я хочу обернуть асинхронный API, который выглядит следующим образом:
[someObject completeTaskWithCompletionHandler:^(NSString *result) {
}];
в синхронный метод, который я могу назвать следующим образом
NSString *result = [someObject completeTaskSynchronously];
Как мне это сделать? Я сделал некоторое чтение документа и поиск в Google и попытался использовать "dispatch_semaphore", чтобы попытаться добиться его так:
-(NSString *) completeTaskSynchronously {
__block NSString *returnResult;
self.semaphore = dispatch_semaphore_create(0);
[self completeTaskWithCompletionHandler:^(NSString *result) {
resultResult = result;
dispatch_semaphore_signal(self.semaphore);
}];
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
return resultResult;
}
Но это, похоже, не работает, оно в основном останавливается на dispatch_semaphore_wait. Выполнение никогда не достигает внутреннего блока, который выполняет _signal. У кого-нибудь есть пример кода о том, как это сделать? Я подозреваю, что блок должен быть на другом потоке другого основного потока? Кроме того, предположим, что у меня нет доступа к исходному коду за методом async. Спасибо!
Ответы
Ответ 1
dispatch_semaphore_wait
блокирует основную очередь в вашем примере. Вы можете отправить асинхронную задачу в другую очередь:
__block NSString *returnResult;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue,^{
result = [someObject completeTaskSynchronously];
});
Или используйте какую-либо другую систему, например NSRunLoop:
__block finished = NO;
[self completeTaskWithCompletionHandler:^(NSString *result) {
resultResult = result;
finished = YES;
}];
while (!finished) {
// wait 1 second for the task to finish (you are wasting time waiting here)
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
Ответ 2
Использование NSRunLoop проще всего здесь.
__block NSString* result = nil;
[self completeTaskWithCompletionHandler:^(NSString *resultstring) {
result = resultstring;
}];
while (!result) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
Ответ 3
Я думаю, что лучшим решением будет NSRunLoop, как показано ниже. Его простой и эффективный способ.
- (NSString *)getValue {
__block BOOL _completed = NO;
__block NSString *mValue = nil;
[self doSomethingWithCompletionHandler:^(id __nullable value, NSError * __nullable error) {
mValue = value;
_completed = YES;
}];
while (!_completed) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
return mValue;
}
Ответ 4
Вы можете попытаться использовать NSOperations, чтобы это делало ваши вещи асинхронно.