我有兴趣在模型对象的属性上注册我的视图控制器以获取KVO通知。
视图控制器的“member”属性是NSManagedObject子类,它使用Core Data提供的访问器方法(通过@dynamic
)。它有四个属性:firstName,lastName,nickname和bio,它们都是NSStrings。
以下是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或昵称时收到四个通知。这通常是三个太多的通知!
我做错了似乎很简单,但我无法弄清楚是什么导致无关的通知。如果我更改字典,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个通知(每个属性一个,新值为null,最后是预期的通知)。
答案 0 :(得分:2)
所以我的问题的答案是我的单元测试的问题。在我的setup方法中,我正在创建托管对象上下文,将托管对象插入其中,然后在我的-setUp
方法结束时对上下文进行dealloc。
如果我在测试套件中将MOC作为ivar保留,则通知会按预期进行。
这提出了所有其他问题,但我会在其他时间留下这些问题。现在,似乎我应该关掉电脑,拿一两杯威士忌......或者三个。
答案 1 :(得分:0)
您正在此注册:kFHMemberDetailContext
您正在检查此上下文的不等式:kFHMemberDetailTableViewControllerContext
当然,这些都是常数,它们可能也是一样的。
因此,您可能总是会调用super
实现。也许这就是导致你的额外通知的原因。
作为一个实用的解决方案,检查新密钥,如果它为空,则只需提前返回。