2013-02-14 24 views
1

我有兴趣在模型对象的属性中注册我的视图控制器以用于KVO通知。KVO +托管对象 - 接收太多通知

视图控制器的“member”属性是一个NSManagedObject子类,它使用Core Data提供的访问器方法(通过@dynamic)。它有四个属性:firstName,lastName,nickname和bio,它们都是NSString。

下面是KVO的注册和注销:

- (void)viewWillAppear:(BOOL)animated 
{ 
    [super viewWillAppear:animated]; 

    [self.member addObserver:self 
        forKeyPath:@"firstName" 
        options:NSKeyValueObservingOptionNew 
        context:kFHMemberDetailContext]; 

    [self.member addObserver:self 
        forKeyPath:@"lastName" 
        options:NSKeyValueObservingOptionNew 
        context:kMemberDetailContext]; 

    [self.member addObserver:self 
        forKeyPath:@"nickname" 
        options:NSKeyValueObservingOptionNew 
        context:kMemberDetailContext]; 
} 

- (void)viewWillDisappear:(BOOL)animated 
{ 
    [super viewWillDisappear:animated]; 

    [self.member removeObserver:self forKeyPath:@"firstName" context:kMemberDetailContext]; 
    [self.member removeObserver:self forKeyPath:@"lastName" context:kMemberDetailContext]; 
    [self.member removeObserver:self forKeyPath:@"nickname" context:kMemberDetailContext]; 
} 

回调方法的实现

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
{ 
    if (context != kFHMemberDetailContext) { 
     [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 
     return; 
    } 

    self.kvoCount++; 

    if ([keyPath isEqualToString:@"firstName"]) { 
     NSLog(@"firstName KVO'd"); 
    } 
    else if ([keyPath isEqualToString:@"lastName"]) { 
     NSLog(@"lastName KVO'd"); 
    } 
    else if ([keyPath isEqualToString:@"nickname"]) { 
     NSLog(@"nickname KVO'd"); 
    } 
} 

当我驱动从一个单元测试该代码时,收到三个通知,当我修改“bio”属性以及修改firstName,lastName或昵称时的四个通知。这通常是三个太多的通知!

这似乎是简单的,我做错了,但我无法弄清楚是什么导致无关的通知。如果我po更改字典,NSKeyValueChangeKindKey总是NSKeyValueChangeSetting,但对于不需要的通知NSKeyValueChangeNewKey是NULL。

测试驱动该位的代码:

- (void)setUp 
{ 
    NSManagedObjectModel *mom = [NSManagedObjectModel mergedModelFromBundles:@[[NSBundle mainBundle]]]; 
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom]; 
    [psc addPersistentStoreWithType:NSInMemoryStoreType 
         configuration:nil 
           URL:nil 
          options:nil 
           error:NULL]; 
    NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; 
    [managedObjectContext setPersistentStoreCoordinator:psc]; 
    member = [NSEntityDescription insertNewObjectForEntityForName:@"Member" inManagedObjectContext:managedObjectContext]; 

    UIStoryboard *sb = [UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:[NSBundle mainBundle]]; 
    memberDetailVC = [sb instantiateViewControllerWithIdentifier:kFHMemberDetailTableViewControllerIdentifier]; 
    [memberDetailVC setMember:member]; 
} 

- (void)tearDown 
{ 
    [memberDetailVC viewWillDisappear:NO]; 
} 

- (void)testChangesToMemberFirstNamePropertyCausesKVO 
{ 
    [memberDetailVC viewWillAppear:NO]; 

    [member setFirstName:@"Unit Test"]; 

    STAssertTrue(memberDetailVC.kvoCount, (NSInteger)1, @"View controller should have received a single KVO notification"); 
} 

就像我说,这个失败已经接收4个通知(一个用于与空的新的值的每个属性,然后最终预期的通知) 。

回答

2

所以我的问题的答案是我的单元测试的问题。在我的设置方法中,我创建了托管对象上下文,在其中插入了托管对象,然后在我的-setUp方法末尾释放了上下文。

如果我在测试套件中坚持MOC作为ivar,则通知按预期进入。

这引发了所有其他问题,但我会在其他时间留下这些问题。现在看来我应该关掉电脑,然后喝一两杯威士忌......或者三杯。

0

您正在这里注册:kFHMemberDetailContext
您正在检查此上下文中的不等式:kFHMemberDetailTableViewControllerContext
当然,这些是常数,它们也可能是相同的。

因此,您可能会始终致电super实施。也许这就是导致你的额外通知的原因。

作为一种实用的解决方案,请检查新密钥,如果它为空,则只需返回提前。

+0

上下文变量是复制+粘贴错误;对于那个很抱歉!我已经添加了测试代码,看看是否可以揭示我做错的任何事情。我当然可以按照你的建议解决这个问题,但我想知道为什么它首先发生,看看我是否做了不正确的事情! – edelaney05 2013-02-14 13:20:15

+1

另外,注册通知的更好的钩子在'viewDidAppear'中。此外,请确保在您的单元测试中没有任何其他代码不在您的代码中(例如循环)或从不同测试中重复更改。 – Mundi 2013-02-14 13:26:17

相关问题