Как обернуть асинхронный метод, который принимает блок и превратить его синхронно в объективе 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, чтобы это делало ваши вещи асинхронно.