2

モデル オブジェクトのプロパティの KVO 通知用にビュー コントローラーを登録することに興味があります。

ビュー コントローラーの「メンバー」プロパティは NSManagedObject サブクラスであり、Core Data の提供されたアクセサー メソッドを ( 経由で@dynamic) 使用します。これには、firstName、lastName、nickname、bio の 4 つのプロパティがあり、これらはすべて 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」プロパティを変更すると 3 つの通知を受け取り、firstName、lastName、またはニックネームを変更すると 4 つの通知を受け取ります。これは常に 3 つの通知が多すぎます。

私が間違っていたのは単純なことのようですが、無関係な通知の原因はわかりません。変更ディクショナリを指定すると、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 のプロパティごとに 1 つ、最後に予想される通知) を受信したために失敗します。

4

2 に答える 2

2

したがって、私の問題に対する答えは、ユニットテストの問題です。セットアップメソッドでは、管理対象オブジェクトコンテキストを作成し、それに管理対象オブジェクトを挿入すると、-setUpメソッドの最後でコンテキストが割り当て解除されます。

テストスイートでivarとしてMOCを保持すると、通知は期待どおりに届きます。

これは他のあらゆる種類の質問を提起しますが、私はそれらを別の機会に残しておきます。今のところ、私はコンピューターの電源を切り、ウイスキーを1つか2つ...または3つ持って行くべきだと思われます。

于 2013-02-14T21:39:19.453 に答える
0

このコンテキストで登録しています: kFHMemberDetailContext
このコンテキストの不等式をチェックしています: kFHMemberDetailTableViewControllerContext
もちろん、これらは定数なので、同じかもしれません。

したがって、常にsuper実装を呼び出す可能性があります。おそらく、それが追加の通知の原因です。

実用的な解決策として、新しいキーを確認し、null の場合は単純に早期に戻ります。

于 2013-02-14T11:09:23.930 に答える