2012-04-07 37 views
7

Tôi muốn được thông báo, khi đếm, nghĩa là. số lượng các mục trong một thay đổi NSArray .. Tất nhiên tôi sẽ không cần điều này, nếu tôi đã kiểm soát việc bổ sung và loại bỏ các đối tượng vào mảng. Nhưng tôi không, nó xảy ra không thể đoán trước liên quan đến mô hình quy trình nghiệp vụ và phụ thuộc vào các yếu tố bên ngoài. Có một số giải pháp đơn giản, thanh lịch không?Số quan sát trong NSMutableArray

EDIT: Tôi đang chỉnh này để NSMutableArray tất nhiên ..

+0

Tôi không phải là 100% về điều này, nhưng một keyPath cho một mảng và hậu tố '@ count' là cách KVC để có được giá trị này. Vì vậy, có lẽ bạn có thể KVO quan sát 'mảng @ đếm'? https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueCoding/Articles/CollectionOperators.html#//apple_ref/doc/uid/20002176-BAJEAIEE – joerick

Trả lời

15

Bạn sẽ cần phải sử dụng KVC. Nhưng làm thế nào để đi về làm điều đó? Sau khi tất cả, NSMutableArray không phải là Key-Value-Coding phù hợp với các phương pháp đột biến của nó hoặc thay đổi nội dung. Câu trả lời là proxy-subclassing NS [Mutable] Array là quá nhiều rắc rối.

NSProxy là một lớp nhỏ tuyệt vời mà bạn có thể sử dụng để chặn các thư được gửi tới mảng của bạn như thể bạn là NSMutableArray, sau đó chuyển tiếp chúng vào một số cá thể nội bộ. Thật không may, nó cũng không phải là KVC tuân thủ, như là ruột của KVC sống trong NSObject. Sau đó, chúng ta sẽ phải sử dụng nó. Một giao diện mẫu có thể trông như thế này:

@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 

Như bạn có thể thấy, chúng ta đang mô phỏng một giao diện cho NSMutableArray, đó là cần thiết, như proxy của chúng tôi nên thực hiện tất cả mọi thứ như thể đó là một NSMutableArray. Điều này cũng làm cho việc triển khai càng đơn giản càng tốt, vì chúng ta chỉ có thể chuyển tiếp các bộ chọn đến con trỏ bên trong của chúng ta là NSMutableArray. Vì lợi ích của ngắn gọn, tôi sẽ chỉ thực hiện hai phương pháp để bạn thấy những gì một phác thảo chung hình như:

@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 

Nếu bạn không có cơ hội để quấn một mảng như thế này, sau đó cố gắng suy nghĩ lại mã của bạn . Nếu một phụ thuộc bên ngoài đang buộc bạn vào loại góc này, hãy cố gắng loại bỏ nó. Luôn luôn là một điều xấu để làm việc xung quanh các công cụ của riêng bạn.

+0

Một thời gian trôi qua và một cải tiến may mắn sẽ được thực hiện trên một phần của mã nằm ngoài tầm kiểm soát của tôi - đối tượng mặt tiền chứa mảng sẽ phát hành NSNotifications khi mô hình thay đổi. Giải pháp được đề xuất của bạn là đáng chú ý tuy nhiên bạn đã bỏ lỡ một thực tế nhỏ nhưng quan trọng là tôi không kiểm soát được mảng. Vì vậy, tôi không thể tiêm đối tượng proxy vào mặt tiền thay vì mảng. –

+0

Tôi cần học được điều mới mẻ. – naz

+0

@CodaFi Cảm ơn bạn đã giải thích :) –

6

Chấp hành thay đổi trong một mutableArray nhu cầu sử dụng đối tượng proxy có thể thay đổi do

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

đó là KVO tuân thủ, ví dụ: bất kỳ sự thay đổi của đối tượng proxy gửi ý/đã làm thay đổi thông báo.

Lớp giới thiệu sau đây thể hiện đầy đủ việc thực hiện

@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 

Bây giờ mỗi khi bạn thêm hoặc xóa một đối tượng trong items bạn sẽ thấy log mới trong giao diện điều khiển (observeValueForKeyPath được gọi).

Bất kỳ thay đổi trực tiếp nào của ivar tự động tổng hợp _items mảng sẽ không có hiệu lực.

Cũng lưu ý rằng bạn cần đặt người quan sát trên [email protected] (quan sát [email protected] là vô nghĩa).

Lưu ý rằng bạn không cần phải init _items hoặc self.items. Nó sẽ được thực hiện sau khi bạn gọi items getter.

Mỗi khi bạn thay đổi "mảng" items, bạn sẽ nhận được đối tượng mới _items với địa chỉ mới. Nhưng tôi vẫn có thể tìm thấy nó qua items proxy getter.

+0

Bạn cũng có thể muốn có phương thức 'removeProxyItemsObserver:'. –

+0

@AaronBrager, chỉ cần thêm cho đầy đủ. Cảm ơn. – malex

Các vấn đề liên quan