76

クラスには、( 経由で) 合成されたアクセサーを持つ NSMutableArray 型のプロパティ (およびインスタンス var) があります@property。以下を使用してこの配列を観察する場合:

[myObj addObserver:self forKeyPath:@"theArray" options:0 context:NULL];

そして、次のように配列にオブジェクトを挿入します。

[myObj.theArray addObject:NSString.string];

observeValueForKeyPath... 通知は送信されません。ただし、以下は適切な通知を送信します。

[[myObj mutableArrayValueForKey:@"theArray"] addObject:NSString.string];

これは、mutableArrayValueForKeyオブザーバーへの通知を処理するプロキシ オブジェクトを返すためです。

しかし、合成されたアクセサーは、そのようなプロキシ オブジェクトを自動的に返すべきではありませんか? これを回避する適切な方法は何ですか? を呼び出すだけのカスタム アクセサーを作成する必要があります[super mutableArrayValueForKey...]か?

4

7 に答える 7

79

しかし、合成されたアクセサーはそのようなプロキシオブジェクトを自動的に返すべきではありませんか?

いいえ。

これを回避する適切な方法は何ですか?呼び出すだけのカスタムアクセサーを作成する必要があります[super mutableArrayValueForKey...]か?

いいえ。アレイアクセサーを実装します。これらを呼び出すと、KVOは適切な通知を自動的に投稿します。だからあなたがしなければならないのは:

[myObject insertObject:newObject inTheArrayAtIndex:[myObject countOfTheArray]];

そして正しいことは自動的に起こります。

便宜上、addTheArrayObject:アクセサーを作成できます。このアクセサーは、上記の実際のアレイアクセサーの1つを呼び出します。

- (void) addTheArrayObject:(NSObject *) newObject {
    [self insertObject:newObject inTheArrayAtIndex:[self countOfTheArray]];
}

(の代わりに、配列内のオブジェクトの適切なクラスを入力できます。入力する必要がありNSObjectます。)

次に、の代わりに[myObject insertObject:…]、を記述し[myObject addTheArrayObject:newObject]ます。

悲しいことに、最後に確認したところ、配列プロパティではなく、set( add<Key>Object:NSSetremove<Key>Object:のように)プロパティについてのみKVOによって認識されるため、認識されるアクセサーの上に実装しない限り、無料のKVO通知を受け取ることはありません。 。私はこれについてバグを提出しました:x-radar:// problem / 6407437

ブログにすべてのアクセサーセレクター形式のリストがあります。

于 2008-11-20T02:10:56.263 に答える
9

この状況ではwillChangeValueForKeyandは使用しません。didChangeValueForKey1 つは、対多関係の値が変化していることではなく、そのパスの値が変化したことを示すことです。willChange:valuesAtIndexes:forKey:このようにした場合は、代わりに使用したいと思うでしょう。それでも、このような手動の KVO 通知を使用することは、カプセル化として不適切です。それを行うためのより良い方法はaddSomeObject:、実際に配列を所有するクラスでメソッドを定義することです。これには、手動の KVO 通知が含まれます。このように、配列にオブジェクトを追加する外部メソッドは、配列所有者の KVO の処理についても心配する必要がありません。これはあまり直感的ではなく、オブジェクトをいくつかの場所から配列します。

この例では、実際に を引き続き使用しますmutableArrayValueForKey:。私は変更可能な配列に肯定的ではありませんが、ドキュメントを読むと、このメソッドは実際には配列全体を新しいオブジェクトに置き換えると信じているため、パフォーマンスが懸念される場合は、配列を所有するクラスにinsertObject:in<Key>AtIndex:も実装する必要がありますremoveObjectFrom<Key>AtIndex:.

于 2008-11-19T19:51:49.853 に答える
3

あなた自身の質問に対するあなた自身の答えはほぼ正しいです。theArray外部に販売しないでください。代わりに、theMutableArrayインスタンス変数に対応しない別のプロパティ を宣言し、次のアクセサーを記述します。

- (NSMutableArray*) theMutableArray {
    return [self mutableArrayValueForKey:@"theArray"];
}

その結果、他のオブジェクトを使用thisObject.theMutableArrayして配列を変更できるようになり、これらの変更が KVO をトリガーします。

insertObject:inTheArrayAtIndex:他の回答は、実装しremoveObjectFromTheArrayAtIndex:ても正しい場合に効率が向上することを指摘しています。ただし、他のオブジェクトがこれらを認識したり、直接呼び出したりする必要はありません。

于 2010-06-17T04:02:28.597 に答える
0

addObject:呼び出しwillChangeValueForKey:と呼び出しをラップする必要がありdidChangeValueForKey:ます。私の知る限り、変更しようとしている NSMutableArray がその所有者を見ているオブザーバーについて知る方法はありません。

于 2008-11-19T17:49:41.813 に答える
0

1 つの解決策は、NSArray を使用し、次のように挿入および削除してゼロから作成することです。

- (void)addSomeObject:(id)object {
    self.myArray = [self.myArray arrayByAddingObject:object];
}

- (void)removeSomeObject:(id)object {
    NSMutableArray * ma = [self.myArray mutableCopy];
    [ma removeObject:object];
    self.myArray = ma;
}

KVOを取得して、古いアレイと新しいアレイを比較できるよりも

注: self.myArray は nil であってはなりません。それ以外の場合、arrayByAddingObject: 結果も nil になります。

場合によっては、これが解決策になる可能性があります。NSArray はポインターのみを格納しているため、大規模な配列や頻繁な操作を使用しない限り、オーバーヘッドはそれほど大きくありません。

于 2013-10-11T08:35:28.257 に答える