Ios4 GCD和回调-并发问题

Ios4 GCD和回调-并发问题,ios4,grand-central-dispatch,abaddressbook,Ios4,Grand Central Dispatch,Abaddressbook,我注册了一个回调处理程序,用于侦听iOS通讯簿中的更改。由于某些奇怪的原因(其中存在一个bug),当应用程序从后台返回时,有时会多次调用此回调。我希望回调处理程序只运行一次它的逻辑,即使在多次调用回调的情况下也是如此。这是我注册回调的方式: ABAddressBookRegisterExternalChangeCallback(address_book, adressBookChanged, self); 这就是我如何构造回调处理程序,以利用GCD来处理这个问题。不幸的是,它不起作用,GCD也

我注册了一个回调处理程序,用于侦听iOS通讯簿中的更改。由于某些奇怪的原因(其中存在一个bug),当应用程序从后台返回时,有时会多次调用此回调。我希望回调处理程序只运行一次它的逻辑,即使在多次调用回调的情况下也是如此。这是我注册回调的方式:

ABAddressBookRegisterExternalChangeCallback(address_book, adressBookChanged, self);
这就是我如何构造回调处理程序,以利用GCD来处理这个问题。不幸的是,它不起作用,GCD也不能阻止内部逻辑被调用两次

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

我很确定这段代码适用于接收多个通知,那么回调是否不同呢?它们是否会自动生成不同的线程,使fireOnce值每次都为FALSE?我应该如何编写此代码以防止多个回调多次调用内部逻辑?我想我可以使用锁和/或同步块来实现这一点,但GCD似乎是实现这一点的更干净的方法

无论您试图使用GCD做什么,您都在否定它的任何影响,因为您每次调用回调时都要创建一个队列,并且该队列与其他队列不同,因此它总是运行。您可能意味着在回调外部创建队列,并在回调内部使用它(可能是静态全局的?)


不过,我不明白这对您有什么帮助,因为每次启动回调时,您仍然会运行每个GCD块。除非您的
do internal logic
部分将记录标记为已更新,并且您在影响同一记录的排队方法中检查此标志,否则您仍将多次运行您的代码,无论GCD与否。

实际上并不是对您的GCD问题的直接回答,但我认为,每当您注册时提供一个唯一的“上下文”时,就会创建一个新的“注册”,这样您就可以为每个“上下文”回调。通过提供相同的“上下文”,您可能可以避免多次调用。

我最终使用了NSTimer而不是GCD来防止重复回调触发我的关键方法。简单得多,而且效果相当好

[self.changeTimer invalidate];
self.changeTimer = nil;
self.changeTimer = [NSTimer scheduledTimerWithTimeInterval:3.0
                                                            target:self
                                                          selector:@selector(handleAdressBookExternalCallbackBackground)
                                                          userInfo:nil
                                                           repeats:NO];

要在GDC的帮助下只执行一次代码,可以执行以下操作:

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
    a piece of code
});

多次回调的原因是由于通讯录iCloud后台同步。通常,如果在同一个iCloud帐户中登录了多个设备,则同步将传播到所有设备,并回显到发生更改的测试设备,从而导致多次调用回调


顺便说一句,使用计时器约束重复调用无助于完全解决此问题,因为您不知道下一个回调何时调用取决于您的网络条件。您应该编写逻辑来处理这些重复的调用。

我也有类似的问题。我的解决方案是将标志保存在NSUserDefaults中,在第一个addressbookChanged方法后启用此标志,然后在我的操作完成后再次禁用它

void MyAddressBookExternalChangeCallback (ABAddressBookRef notifyAddressBook,CFDictionaryRef info,void *context)
{
   NSLog(@"in MyAddressBook External Change Callback");

    if([[[NSUserDefaults standardUserDefaults]objectForKey:@"addressBookChanged"] boolValue] == NO)
      {
       [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"addressBookChanged"];
       [[NSUserDefaults standardUserDefaults] synchronize];

        //we save sync status to defaults to prevent duplicate call of this method

        [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"addressBookSync"];
        [[NSUserDefaults standardUserDefaults]synchronize];

        [APICallWithCompletion:^(BOOL success, id object) {
            [[NSUserDefaults standardUserDefaults] setObject:@NO forKey:@"addressBookChanged"];
            [[NSUserDefaults standardUserDefaults] synchronize];
        }];
   }
}
虽然这可能不是正确的方法,但它似乎对我有效,因为我的api调用需要足够长的时间来防止重复调用此方法。。。我想你可以把它换成

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    [[NSUserDefaults standardUserDefaults] setObject:@NO forKey:@"addressBookChanged"];
    [[NSUserDefaults standardUserDefaults] synchronize];
});

我花了将近两天的时间研究这个问题。甚至我也在使用定时器,但这会产生更多的问题。例如,如果你将计时器设置为5秒,并且在这段时间内,如果你再次访问联系人并进行一些更改,然后访问应用程序,它将最终忽略该更改,因为5秒还没有结束。因此,对于该更改,您必须关闭该应用程序并重新运行该应用程序。 我只做了两步,一切都像魔术一样 在

我正在注册外部更改的方法

    -(void) registerExternalChanges
{
    dispatch_async(dispatch_get_main_queue(), ^{
        ABAddressBookRef addressBookRef = [self takeAddressBookPermission];
        ABAddressBookRegisterExternalChangeCallback(addressBookRef, addressBookChanged , (__bridge void *)(self));
    });
}
一旦你在联系人数据库中完成更改后进入应用程序 注销外部更改

 ABAddressBookUnregisterExternalChangeCallback(ntificationaddressbook, addressBookChanged,(context));

也就是说,addressBookChanged方法只会被调用一次

您是否有关于多次收到回调的错误的参考?9301976。由于“信息不足”,它在一段时间前关闭了,这基本上意味着他们要求一个样本项目来复制这个问题,而这不是我可以随意复制给他们的东西。。。有些Exchange错误触发这些警报时会发生这种情况。好的,我可能会安排并发送一个示例应用程序,因为每次运行时都会出现错误。谢谢在应用程序的生命周期内(直到终止)只运行一次代码。这对我们的情况没有帮助,因为我们希望反复处理通讯簿回调。另外,如果设备与iCloud同步,则每次从后台带回应用程序时,都会至少调用一次回调,即使没有对AB进行任何更改,因此,计时器可以防止多个并发调用,但不能防止每次应用程序返回时都发生此“幻影”调用。
 ABAddressBookUnregisterExternalChangeCallback(ntificationaddressbook, addressBookChanged,(context));