how to wrap an asynchronous method that takes a block and turn it synchronous in objective c

与世无争的帅哥 提交于 2019-12-20 09:59:18

问题


I want to wrap an async API that look like this:

[someObject completeTaskWithCompletionHandler:^(NSString *result) {

}];

into a synchronous method that i can call like this

NSString *result = [someObject completeTaskSynchronously];

How do I do this? I did some doc reading and google search, and attempt to use "dispatch_semaphore" to do try to achieve it like so:

-(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;
}

But this doesnt seem to work, it basically just halt at dispatch_semaphore_wait. Execution never reaches inside block that do the _signal. Anyone has code example on how to do this? I suspect that the block has to be on a different thread other the main thread? Also, assume i don't have access to the source code behind the async method. Thanks!


回答1:


dispatch_semaphore_wait blocks the main queue in your example. You can dispatch the async task to a different queue:

__block NSString *returnResult;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue,^{
     result = [someObject completeTaskSynchronously];
});

Or use some other system, like 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:


Using an NSRunLoop is the easiest to do here.

__block NSString* result = nil;
[self completeTaskWithCompletionHandler:^(NSString *resultstring) {
    result = resultstring;
}];
while (!result) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}



回答3:


I think the better solution will be NSRunLoop as given below. Its simple and working fine.

- (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:


You can try to use NSOperations for this doing your things asynchronously.



来源:https://stackoverflow.com/questions/20604857/how-to-wrap-an-asynchronous-method-that-takes-a-block-and-turn-it-synchronous-in

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!