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