Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCD and callbacks - concurrency issue

I have a callback handler registered that listens to changes in the iOS Address Book. Due to some strange reason (for which a bug has been filed), this callback can sometimes be called more than once when the app returns from the background. I want my callback handler to run it's logic only once, even in cases the callback is called multiple times. This is how I register the callback:

ABAddressBookRegisterExternalChangeCallback(address_book, adressBookChanged, self);

This is how I structured my callback handler to take advantage of GCD to handle this. Unfortunately, it's not working, and GCD doesn't prevent the internal logic to be called twice...

void adressBookChanged(ABAddressBookRef ab, CFDictionaryRef info, void 
                       *context) 
{ 
    NSLog(@"** IN addressBookChanged callback!");

    ABAddressBookUnregisterExternalChangeCallback (ab, adressBookChanged, context);

    __block BOOL fireOnce = FALSE;
    dispatch_queue_t queue;
    queue = dispatch_queue_create("com.myapp.abcallback", NULL);

    dispatch_async(queue, ^{

        if (fireOnce == FALSE) {

            fireOnce = TRUE;

            dispatch_queue_t queueInternal;
            queueInternal = dispatch_queue_create("com.myapp.abcallbackInternal", NULL);
            dispatch_async (queueInternal, ^{
               NSLog(@"do internal logic");

            });

            dispatch_release(queueInternal);
        }
    });
    dispatch_release(queue);
}

I'm pretty sure this code works for receiving multiple notifications, so are callbacks different? Do they spawn different threads automatically, making the fireOnce value to be FALSE each time? How should I write this code to prevent multiple callbacks from calling the internal logic more than once? I suppose I could use locks and/or synchronized blocks to achieve this, but GCD seemed like a cleaner way to achieve this.

like image 883
Z S Avatar asked Aug 19 '11 04:08

Z S


Video Answer


2 Answers

The cause of multiple callbacks is due to the phone book iCloud background synchronization. Usually, if you have multiple devices logged in a same iCloud account, the synchronization will propagate to all devices, and echoed back to your testing device from where the change originated, thus, causes the callback to be invoked multiple times.

By the way, using a timer to constraint the duplicated invocations won't help resolve this issue completely, because you don't know when the next callback will be called depending on your network condition. You should instead program the logic to handle these duplicated invocations.

like image 54
utogaria Avatar answered Jan 03 '23 17:01

utogaria


I ended up using NSTimers instead of GCD to prevent the duplicate callbacks from firing my critical method. Much simpler, and works quite well!

[self.changeTimer invalidate];
self.changeTimer = nil;
self.changeTimer = [NSTimer scheduledTimerWithTimeInterval:3.0
                                                            target:self
                                                          selector:@selector(handleAdressBookExternalCallbackBackground)
                                                          userInfo:nil
                                                           repeats:NO];
like image 27
Z S Avatar answered Jan 03 '23 15:01

Z S