Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make iOS blocks execute synchronously

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;
}
like image 230
Sathya Avatar asked Jul 07 '11 06:07

Sathya


3 Answers

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.

like image 108
dev gr Avatar answered Nov 10 '22 17:11

dev gr


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

like image 35
Daij-Djan Avatar answered Nov 10 '22 16:11

Daij-Djan


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 
    }];
}
like image 2
redisant Avatar answered Nov 10 '22 16:11

redisant