7

カウントしたときに通知を受け取りたい、つまり。NSArray内のアイテムの数が変更されます。もちろん、配列へのオブジェクトの追加と削除を制御している場合は、これは必要ありません。しかし、私はそうではありません。それはビジネスプロセスモデルに関して予期せずに起こり、外部要因に依存します。シンプルでエレガントな解決策はありますか?

編集:もちろん、これをNSMutableArrayに修正しています。

4

2 に答える 2

15

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

このように配列をラップする機会がない場合は、コードを再考してみてください。外部依存がこの種の窮地に追い込まれている場合は、それを取り除くようにしてください。独自のツールを回避することは常に悪いことです。

于 2012-04-07T23:53:02.297 に答える
6

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. itemsgetterを呼び出すと、バックグラウンドで実行されます。

「配列」を変更するたびに、新しいアドレスを持つitems新しいオブジェクトが取得されます。しかし、プロキシゲッター_itemsを介して見つけることができます。items

于 2015-03-21T16:24:07.673 に答える