カウントしたときに通知を受け取りたい、つまり。NSArray内のアイテムの数が変更されます。もちろん、配列へのオブジェクトの追加と削除を制御している場合は、これは必要ありません。しかし、私はそうではありません。それはビジネスプロセスモデルに関して予期せずに起こり、外部要因に依存します。シンプルでエレガントな解決策はありますか?
編集:もちろん、これをNSMutableArrayに修正しています。
カウントしたときに通知を受け取りたい、つまり。NSArray内のアイテムの数が変更されます。もちろん、配列へのオブジェクトの追加と削除を制御している場合は、これは必要ありません。しかし、私はそうではありません。それはビジネスプロセスモデルに関して予期せずに起こり、外部要因に依存します。シンプルでエレガントな解決策はありますか?
編集:もちろん、これをNSMutableArrayに修正しています。
KVCを使用する必要があります。しかし、どうやってそれを行うのですか?結局のところ、NSMutableArray は、そのミューテーション メソッドまたはコンテンツの変更に対してキー値コーディングに準拠していません。答えはプロキシです。NS[Mutable]Array のサブクラス化は非常に面倒です。
NSProxy は、アレイに送信されたメッセージを NSMutableArray であるかのようにインターセプトし、内部インスタンスに転送するために使用できる小さなクラスです。残念ながら、KVC の根幹は NSObject にあるため、KVC にも準拠していません。では、それを使用する必要があります。サンプル インターフェイスは次のようになります。
@interface CFIKVCMutableArrayProxy : NSObject {
NSMutableArray *_innerArray;
}
- (NSUInteger)count;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)addObject:(id)anObject;
- (void)removeLastObject;
- (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
//…
@end
ご覧のとおり、 のインターフェイスをシミュレートしていますNSMutableArray
。これは、プロキシがすべてを であるかのように実装する必要があるためNSMutableArray
です。NSMutableArray
これにより、セレクターを内部ポインターに転送するだけで済むため、実装が可能な限り単純になります。簡潔にするために、2 つのメソッドのみを実装して、一般的なアウトラインがどのようなものかを示します。
@implementation CFIKVCMutableArrayProxy
//…
- (NSUInteger)count {
return _innerArray.count;
}
- (void)addObject:(id)anObject {
[self willChangeValueForKey:@"count"];
[_innerArray addObject:anObject];
[self didChangeValueForKey:@"count"];
}
- (void)removeLastObject {
[self willChangeValueForKey:@"count"];
[_innerArray removeLastObject];
[self didChangeValueForKey:@"count"];
}
@end
このように配列をラップする機会がない場合は、コードを再考してみてください。外部依存がこの種の窮地に追い込まれている場合は、それを取り除くようにしてください。独自のツールを回避することは常に悪いことです。
mutableArray の変更を観察するには、次で指定される可変プロキシ オブジェクトを使用する必要があります。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key
これは KVO に準拠しています。つまり、プロキシ オブジェクトが変更されると、will/did 変更通知が送信されます。
次のデモ クラスは、完全な実装を示しています。
@interface DemoClass : NSObject
@property (nonatomic) NSMutableArray *items;
- (void)addItemsObserver:(id)object;
- (void)removeItemsObserver:(id)object;
@end
@implementation DemoClass
- (NSMutableArray *)items;
{
return [self mutableArrayValueForKey:@"_items"];
}
- (void)addItemsObserver:(id)object
{
[self addObserver:object forKeyPath:@"_items.@count" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}
- (void)removeItemsObserver:(id)object
{
[self removeObserver:object forKeyPath:@"_items.@count" context:nil];
}
@end
@interface ObservingClass : NSObject
@property (nonatomic) DemoClass *demoObject;
@end
@implementation ObservingClass
- (instanstype)init
{
if (self = [super init]) {
_demoObject = [DemoClass new];
[_demoObject addItemsObserver:self];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
NSLog(@"is called on demoObject.items.count change");
}
- (void)dealloc
{
[_demoObject removeItemsObserver:self];
}
@end
これで、オブジェクトを追加または削除するたびにitems
、コンソールに新しいログが表示されます (observeValueForKeyPath
が呼び出されます)。
自動合成された ivar_items
配列を直接変更しても効果はありません。
また、オブザーバーをオンにする必要があることにも注意してください_items.@count
(観察items.@count
は無意味です)。
init_items
またはself.items
. items
getterを呼び出すと、バックグラウンドで実行されます。
「配列」を変更するたびに、新しいアドレスを持つitems
新しいオブジェクトが取得されます。しかし、プロキシゲッター_items
を介して見つけることができます。items