2012-04-07 107 views
7

我希望得到通知,當計數,即。 NSArray中的項目數目發生了變化.. 當然,如果我在控制添加和刪除對象到數組中,當然我不需要這個。但我不是,它在業務流程模型方面發生了不可預測的變化,並取決於外部因素。 有沒有一些簡單優雅的解決方案?在NSMutableArray中觀察計數

編輯:我糾正這NSMutableArray的當然..

+0

我對此並不是100%,但數組的keyPath和後綴'@ count'是KVC獲取此值的方式。所以也許你可以KVO觀察'array @ count'? https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueCoding/Articles/CollectionOperators.html#//apple_ref/doc/uid/20002176-BAJEAIEE – joerick 2012-04-07 23:53:42

回答

15

你需要使用KVC。但如何去做呢?畢竟,NSMutableArray對於其突變方法或內容更改不符合鍵值編碼。答案是代理 - 作爲子類NS [Mutable]數組太麻煩了。

NSProxy是一個偉大的小類,您可以使用,就好像你是一個NSMutableArray攔截髮送到您的陣列中的消息,然後轉發他們到一些內部情況。不幸的是,它也不符合KVC標準,因爲KVC的內核生活在NSObject中。那麼我們必須使用它。樣本接口可能是這個樣子:

@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。爲了簡便起見,我將只實現兩個方法告訴你什麼是一般的輪廓看起來像:

@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 

如果你沒有機會結束這樣一個數組,然後嘗試重新思考你的代碼。如果外部依賴性迫使您進入這種角落,請嘗試刪除它。解決你自己的工具總是一件壞事。

+0

一段時間過去了,幸運的改善將會在不在我控制範圍內的代碼部分實現 - 當模型更改時,包含數組的facade對象現在將發出NSNotifications。 您提出的解決方案是值得注意的,但是您錯過了一點但卻非常重要的事實,即我並未控制陣列。所以我不可能將代理對象注入到外觀而不是數組中。 – 2014-01-30 13:57:07

+0

出於需要,我學到了一些新東西。 – naz 2014-10-03 00:12:32

+0

@CodaFi感謝您的解釋:) – 2016-02-27 13:31:09

6

觀察一個mutableArray一個變化需要通過

- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key 

其使用給定的可變代理對象是符合KVO,即代理對象的任何變化發送意願/確實改變通知。

下面的試聽課每次添加或刪除對象時顯示現在全面推行

@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:@"[email protected]" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil]; 
} 

- (void)removeItemsObserver:(id)object 
{ 
    [self removeObserver:object forKeyPath:@"[email protected]" 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陣列的任何直接變化將沒有任何效果。

另請注意,您強烈需要將觀察者設置爲[email protected](觀察[email protected]是無意義的)。

請注意,您不需要初始化_itemsself.items。當您撥打items獲得者時,將在幕後完成。

每當您更改「數組」items時,您將獲得新對象_items以及新地址。但我仍然可以通過items proxy getter找到它。

+0

您也可能想要'removeProxyItemsObserver:'方法。 – 2015-03-21 16:34:59

+0

@AaronBrager,爲了完整而添加。謝謝。 – malex 2015-03-21 23:45:46