Santosh Gurram
Santosh Gurram

Reputation: 1017

dispatch_async in return method Objective-C

I have been trying to use dispatch_async in a method that returns a result. However, I observed that the method returns before executing the dispatch_async block. Due to this I'm not getting the results I expect. Here is some code that demonstrates my problem.

-(BOOL) isContactExists {
    BOOL isContactExistsInXYZ = YES;
    UserId *userId = contact.userId;
    dispatch_async(dispatch_get_main_queue(), ^
    {
        iOSContact *contact = [iOSContact contactForUserId:userId];
        if (nil == contact)
        {
          isContactExistsInXYZ = NO;
        }
    });    

    return isContactExistsInXYZ;
}

This method isContactExists is called somewhere else and based on the response from that method I have to do some stuff. But every time, the value of isContactExistsInXYZ is not what I expect. How do I handle dispatch_async in this situation?

Upvotes: 8

Views: 13106

Answers (3)

Peter Segerblom
Peter Segerblom

Reputation: 2813

If your going the block route your method needs to look something like this.

- (void)isContactExistsWithCompletionHandler:(void(^)(BOOL exists)) completion 
{
    dispatch_async(dispatch_get_main_queue(), ^
    {
        BOOL isContactExistsInXYZ = YES;
        UserId *userId = contact.userId;
        iOSContact *contact = [iOSContact contactForUserId:userId];
        if (nil == contact)
        {
          isContactExistsInXYZ = NO;
        }
        completion(isContactExistsInXYZ);
    });
}

And where you are calling it something like this.

[someObject isContactExistsWithCompletionHandler:^(BOOL exists) {
    // do something with your BOOL
}];

You should also consider placing your heavy operations in a other que than main. Like this.

- (void)isContactExistsWithCompletionHandler:(void(^)(BOOL exists)) completion 
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL);
    dispatch_async(queue, ^
    {
        BOOL isContactExistsInXYZ = YES;
        UserId *userId = contact.userId;
        iOSContact *contact = [iOSContact contactForUserId:userId];
        if (nil == contact)
        {
          isContactExistsInXYZ = NO;
        }
        dispatch_async(dispatch_get_main_queue(), ^
        {
            completion(isContactExistsInXYZ);
        });
    });
}

Upvotes: 16

Wain
Wain

Reputation: 119031

You need to respect that what you are trying to do is asynchronous and embrace that. This means not using a return value. Instead you can write your method to take a callback block as a parameter. Then, when your asynchronous check is complete you can call the block with the result.


So your method signature would become:

- (void)checkIfContactExistsWithCompletion:(ContactExistsBlock)completion {

Where ContactExistsBlock is a block definition with no return and probably a single BOOL parameter.

typedef void (^ContactExistsBlock) (BOOL exists);

Upvotes: 6

Abhinav
Abhinav

Reputation: 38162

The reason is dispatch_async(dispatch_get_main_queue(), ^does not wait until execution is done. You are probably messing up stuff there. Normally, this is used to update UI asynchronously along with other server content getting downloaded in some other thread. Try using dispatch_sync instead.

Upvotes: 3

Related Questions