天天看點

iOS中KVC與KVO的應用解析

iOS中KVC與KVO的應用解析

一、NSKeyValueCoding(KVC)

1、從一個小例子引入

KVC鍵值編碼是Object-C為我們提供的一種對成員變量指派的方法。在探讨其方法之前,我們先來看一個小例子:

首先,建立一個資料模型model類:

//.h檔案

#import <Foundation/Foundation.h>

@interface Model : NSObject

{

   @public//将成員變量設定為公有的 以便其他檔案有通路權限

   NSString * str;

}

@end

我們在其他檔案中有兩種方法str進行指派和取值:

   Model * model = [[Model alloc]init];

   model->str=@"312";//普通方法指派

   [model setValue:@"321" forKey:@"str"];//kvc指派

   NSLog(@"%@",model->str);//普通方法取值

   NSLog(@"%@",[model valueForKey:@"str"]);//kvc取值

同樣的,對于用@property聲明的變量,使用kvc的效果和使用點文法,setter,getter方法的效果是一樣的。

2、KVC有關函數方法詳解

通過上面的例子,我們已經可以簡單了解KVC是幹什麼的了,下面是一些常用方法。

+ (BOOL)accessInstanceVariablesDirectly;

這個方法類似一個開關,預設傳回為YES,表示支援KVC方式指派,也可以在子類中将其重寫,如果傳回為NO,則再進行KVC會抛出異常。

- (id)valueForKey:(NSString *)key;

通過鍵取值

- (void)setValue:(id)value forKey:(NSString *)key;

通過字元串鍵給成員變量指派

- (BOOL)validateValue:(inout id *)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

系統預設實作的方法,驗證一個鍵值是否有效

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

将取到的值放入一個可變數組中

- (NSMutableOrderedSet *)mutableOrderedSetValueForKey:(NSString *)key NS_AVAILABLE(10_7, 5_0);

将取到的值放入可變的有序集合中

- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;

将取到的值放入可變的集合中

- (id)valueForKeyPath:(NSString *)keyPath;

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;

上面這兩個方法分别是通過路徑指派與取值,資料結構類似地圖,比如在model類中有一個成員變量model2,在Model2類中有一個字元串,我們可以通過如下的方式指派取值

//Model.h

#import "Model2.h"

   @public

   Model2 * model2;

//Model2.h

@interface Model2 : NSObject

@public

   NSString * str2;

//其他檔案

   Model2 * model2 = [[Model2 alloc]init];

   model->model2=model2;

   [model setValue:@"123" forKeyPath:@"model2.str2"];

   NSLog(@"%@",[model valueForKeyPath:@"model2.str2"]);

- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;

- (NSMutableOrderedSet *)mutableOrderedSetValueForKeyPath:(NSString *)keyPath NS_AVAILABLE(10_7, 5_0);

- (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;

上面三個方法與前面類似,隻是是從路徑取值的。

- (id)valueForUndefinedKey:(NSString *)key;

這個方法可以擷取沒有提前定義的成員變量的值,比如運作時建立的,下面這個方法是給未定義的成員變量指派

- (void)setValue:(id)value forUndefinedKey:(NSString *)key;

注意:這兩個方法預設的實作會抛出異常,子類必須重寫才能使用。

- (void)setNilValueForKey:(NSString *)key;

将成員變量置為nil

- (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys;

根據鍵值擷取鍵值對字典

- (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues;

通過字典對成員變量同意指派,經常使用

二、NSKeyValueObservingCustomization(KVO)

KVO是一種消息監聽機制,可以在某個量發生變化的時候将消息傳送給監聽者,是以廣泛用于傳值,界面低耦合等邏輯中。KVO機制的核心是以下三個方法:

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

使用這個方法注冊一個監聽者,參數解釋如下:

observer:監聽者對象

keyPath:監聽的參數

options:監聽選項

context:參數傳遞

監聽的選項枚舉如下:

typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {

   NSKeyValueObservingOptionNew = 0x01,//回調的字典中存放新值

   NSKeyValueObservingOptionOld = 0x02,//回調的字典中存放舊值

   NSKeyValueObservingOptionInitial ,//值改變前進行回調

   NSKeyValueObservingOptionPrior//改變前後都進行回調

};

//回調字典後面會解釋

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

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

這兩個方法都是用來移除監聽者

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

這個方法是監聽對象資料改變時回調的方法,change是一個字典,字典中根據監聽的選項不同,存放不同的值(新或者舊)。context是傳遞的參數。

代碼示例:

- (void)viewDidLoad {

   [super viewDidLoad];

   // Do any additional setup after loading the view, typically from a nib.

    model = [[Model alloc]init];

   //添加監聽者

   [model addObserver:self forKeyPath:@"str" options:NSKeyValueObservingOptionNew context:@"321"];

   [model setValue:@"qw" forKey:@"str"];

- (void)didReceiveMemoryWarning {

   [super didReceiveMemoryWarning];

   // Dispose of any resources that can be recreated.

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

   if ([keyPath isEqualToString:@"str"]) {

       NSLog(@"%@",context);

   }

繼續閱讀