Make iOS blocks execute synchronously

2020-02-20 07:55发布

How can I make a block execute synchronously, or make the function wait for the handler before the return statement, so the data can be passed back from the block?

-(id)performRequest:(id)args
{
__block NSData *data = nil;   

    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
        data = [NSData dataWithData:responseData];
    }];

    return data;
}

6条回答
淡お忘
2楼-- · 2020-02-20 08:09

are you sure you want to do it synchronously ? if yes, you can call (or put) your handler function in your block or use Jignesh advice (and use “performSelectorInMainThread” when your handler is finished and you want to return values.

the asynchronous way is (a little bit) harder, but better as: - it forces you to write clean code (no passing of convenient variables) - you can execute other thing so the users won't wait and think your app is slow.

you should really give it two or three hours to go asynchronous. small pain for full gain. you can also have a look to Key-Value Observing.

查看更多
姐就是有狂的资本
3楼-- · 2020-02-20 08:15

async is almost always better. but if you want synchronous:

-(id)performRequest:(id)args
{
__block NSData *data = nil;   

    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
        data = [NSData dataWithData:responseData];
    }];

    while(!data) {
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
    }
    return data;
}

Disclaimer: CocoaCouchDeveloper says that of course this will only work if the completion block and the runloop are on the same thread. I assumed that because many(most) COMPLETION handler I know work that way but of it is valid in principle.

The above is not thread safe
use a semaphore or something maybe.
also I said I don't promote this

查看更多
仙女界的扛把子
4楼-- · 2020-02-20 08:17

You could do like this.

-(id)performRequest:(id)args
{
__block NSData *data = nil;   

 [xyzclass performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse          *urlResponse, NSError *error) {

     dispatch_sync( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{

     data = [NSData dataWithData:responseData];

     });

 }];

return data;
}
查看更多
小情绪 Triste *
5楼-- · 2020-02-20 08:29

You can do synchronous request in another thread like below code

-(void)performRequest:(id)args
{

 NSURLResponse *response = nil;
 NSError *error = nil;
 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
 data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
}

From Main thread you can call this

[self performSelectorInBackground:@selector(performRequest:) withObject:args];

or else you can do asynchronous request using following method

[NSURLConnection alloc]initWithRequest:request delegate:self];

and implement delegate methods for NSURLConnection

查看更多
6楼-- · 2020-02-20 08:30

You can use semaphores in this case.

-(id)performRequest:(id)args
{
    __block NSData *data = nil;   
     dispatch_semaphore_t sem = dispatch_semaphore_create(0);
     [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
       data = [NSData dataWithData:responseData];
       dispatch_semaphore_signal(sem);
     }];
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    return data;
}

semaphore will block execution of further statements until signal is received, this will make sure that your function does not return prematurely.

查看更多
戒情不戒烟
7楼-- · 2020-02-20 08:30

You could just add a method which processes the returned data and call that in your block:

-(void)performRequest:(id)args{
    __block NSData *data = nil;   

    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
        data = [NSData dataWithData:responseData];
        [self processData:data]; //method does something with the data 
    }];
}
查看更多
登录 后发表回答