我有兴趣为模型对象属性上的 KVO 通知注册我的视图控制器。
视图控制器的“成员”属性是一个 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 或昵称时收到四个通知。这始终是三个太多的通知!
我做错了似乎很简单,但我无法弄清楚是什么导致了无关的通知。如果我更改字典,则 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,最后是预期的通知)。