KVC和KVO實現監聽容器類(數組等)的變化

KVC,即Key-Value Coding,鍵值編碼,簡單地說,就是能夠由key獲取一個object對應的property。舉個例子,若是一個對象object,它有一個屬性item,你能夠經過valueForKey也能夠經過object.item來獲取它,同時它支持縱調用,即假如object有個屬性是個item,item有個屬性score,能夠經過@「item.score」獲取,setValueForKey同理。ios

   CGFloat valueScore,score,valueTotal,total;
    TestObject *object = [[TestObject alloc] init];
    valueScore = [[object valueForKeyPath:@"item.score"] floatValue];
    score = object.item.score;
    valueTotal = [[object valueForKey:@"total"] floatValue];
    total = object.total;

若是咱們定義一個property,系統就已經默認幫咱們實現了KVC,實現原理你們能夠能夠看看ios的message原理,以前已經介紹。至於它的好處,蘋果官方給了一個例子,你們能夠感覺一下:數組

Using Key-Value Coding to Simplify Your Code
You can use key-value coding methods in your own code to generalize implementations. For example, in OS X NSTableView and NSOutlineView objects both associate an identifier string with each of their columns. By making this identifier the same as the key for the property you wish to display, you can significantly simplify your code.
Listing 1 shows an implementation of an NSTableView delegate method without using key-value coding. Listing 2 shows an implementation that takes advantage of key-value coding to return the appropriate value using the column identifier as the key.

Listing 1 Implementation of data-source method without key-value coding 
- (id)tableView:(NSTableView *)tableview objectValueForTableColumn:(id)column row:(NSInteger)row
{ ChildObject
*child = [childrenArray objectAtIndex:row]; if ([[column identifier] isEqualToString:@"name"]) { return [child name]; } if ([[column identifier] isEqualToString:@"age"]) { return [child age]; } if ([[column identifier] isEqualToString:@"favoriteColor"]) { return [child favoriteColor]; } // And so on. } Listing 2 Implementation of data-source method with key-value coding  - (id)tableView:(NSTableView *)tableview objectValueForTableColumn:(id)column row:(NSInteger)row { ChildObject *child = [childrenArray objectAtIndex:row]; return [child valueForKey:[column identifier]]; }

 

在某種意義上,KVC是爲了KVO的實現,下面重點說說KVO。KVO,即Key-Value Observing,也就是一個觀察者模式,當一個對象的屬性變化會收到對應的變化通知。變化的種類有四種,app

typedef NS_OPTIONS(NSUInteger, NSKeyValueChange) {

    NSKeyValueChangeSetting = 1,

    NSKeyValueChangeInsertion = 2,

    NSKeyValueChangeRemoval = 3,

    NSKeyValueChangeReplacement = 4

};
其中NSKeyValueChangeSetting主要是對於一個對象地址的變化(或常量的變化),然後三個主要是對容器類的改變,舉個例子,就是一個數組裏新加一個item或者刪掉一個item都會收到對應的通知,這就是kvo的強大之處了。
主要實現代碼:
監聽property:
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

keyPath就是對應的property的string,context就是容許本身攜帶的上下文,在回調裏會返回。ide

移除property的監聽:函數

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context

有一點必須注意,假如你沒有監聽這個property而調用移除,會致使系統崩潰。這裏須要很是當心。this

而後重寫這個回調,當屬性改變就會回調這個函數:編碼

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;

keyPath就是對應的property,context就是監聽手動設置的上下文,能夠爲NULL,字典裏能夠取到變化的type,原來的值和如今的值。atom

 

而要實現數組(或容器)裏面item個數的變化,就要手動重寫兩個函數支持。下面舉個例子:spa

(TestItem有一個數組array,須要實現監聽這個數組裏內容數據的變化)code

@interface TestItem : NSObject

@property(nonatomic,retain)NSMutableArray *array;

@end

@implementation TestItem


-(void)insertObject:(id)object inArrayAtIndex:(NSUInteger)index //這個是表明property名字,就是上面定義的array,系統會自動生成,要根據本身定義的屬性名字改變。

{

    [self.array insertObject:object atIndex:index];

}

-(void)removeObjectFromArrayAtIndex:(NSUInteger)index

{

    [self.array removeObjectAtIndex:index];

}
@end

須要特別主要的是,insertObject和removeObjectFromArrayAtIndex這兩個函數都必須同時實現,不然是無效的。

這裏只實現了這兩個必須實現的函數,剩下的函數看功能須要:

-(void)insertArray:(NSArray *)array atIndexes:(NSIndexSet *)indexes{}

-(void)removeArrayAtIndexes:(NSIndexSet *)indexes{}

-(void)replaceArrayAtIndexes:(NSIndexSet*)indexes withArray:(NSArray *)array{}

-(void)replaceObjectInArrayAtIndex:(NSUInteger)index withObject:(id)object{}

 

固然,有點不方便的是,當咱們要獲取這個TestItem的array時,不能簡單用item.array,而必須調用

  [[item mutableArrayValueForKey:@"array"] addObject:item];

只有用mutableArrayValueForKey獲得的數組變化纔會收到通知。

咱們能夠看看這樣變化產生的change:

(lldb) po change
$6 = 0x09169bc0 {
    indexes = "<NSIndexSet: 0x9169a10>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
    kind = 2;
    new =     (
        "<TestItem: 0x7163cf0>"
    );
}

indexes描述了變化的index,kind = 2即上面所說的NSKeyValueChangeInsertion,表明數組新插入了一個數據,new就是一個增長的數據數組。其餘兩種類型的變化你們能夠感覺一下。

至於NSmutableDictionary的實現原理同樣,就是把array換成dictionary便可。

 

最後,KVO能夠有依賴屬性,便可以實現一個property能夠影響其餘property的變化而產生kvo的通知:

+(NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key

{

    NSSet *set = [superkeyPathsForValuesAffectingValueForKey:key];

    if ([key isEqualToString:@"firstName"]) {

        NSArray *affectingKeys = @[@"fullName"];

        set = [set setByAddingObjectsFromArray:affectingKeys];

    }

    return set;

}

實現了若是firstName改變,也會產生fullName改變的通知。

 

僅供參考。歡迎指導。

相關文章
相關標籤/搜索