KVO與KVC

KVC是什么

KVC 全稱Key-Value Coding,俗稱"鍵值編碼"。它可以通過一個字符串(key)來訪問屬性或者成員變量。

  • 在蘋果的官方文檔中是這樣描述KVC的:它是一種通過字符串描述符而不是通過調用訪問方法或者直接使用實例變量的非直接的訪問對象屬性的機制。
  • 對于KVC的基本的方法都定義在NSKeyValueCoding的非正式協議中,并且NSObject默認實現了該協議。
  • KVC不僅支持對象類型,也支持數值類型和結構體。非對象類型的參數和返回類型會自動封裝成NSValue或NSNumber類型。

KVC常用api

//賦值
-(void)setValue:(id)value forKeyPath:(NSString *)keyPath; 
-(void)setValue:(id)value forKey:(NSString *)key;

//取值
-(id)valueForKeyPath:(NSSting *)keyPath;
-(id)valueForKey:(NSSting *)key;

KVC原理

kvc在取值和賦值的時候是按照一定的順序進行查找的

賦值原理

賦值原理圖.png

取值原理
取值原理圖.png

注意:
1、kvc在賦值和取值的時候是按照順序進行的,可以自己建一個類把對應方法寫進去看下執行及順序。
2、賦值時如果找不到成員變量,不實現setValue:forUndefinedKey:方法會崩潰
如果成員變量是int等基本數據類型,會自動包裝成NSNumber類型返回
3、取值時如果找不到成員變量,不實現valueForUndefinedKey方法會崩潰
如果成員變量是int等基本數據類型,賦值nil,不實現setNilValueForKey:會崩潰

KVO是什么

KVO 全稱Key-Value Observing,俗稱"鍵值監聽",可以用于監聽某個對象屬性值的改變

KVC常用api

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

KVC原理

栗子:創建一個MJPerson類的對象,添加監聽

- (void)viewDidLoad {
    [super viewDidLoad];
    self.person1 = [[MJPerson alloc] init];
    self.person1.age = 1;
    // 給person1對象添加KVO監聽
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.person1.age = 20;
}

// 當監聽對象的屬性值發生改變時,就會調用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    NSLog(@"監聽到%@的%@屬性值改變了 - %@ - %@", object, keyPath, change, context);
}

- (void)dealloc {
    //移除監聽
    [self.person1 removeObserver:self forKeyPath:@"age"];
}

OC在運行時的時候自動生成了一個NSKVONotifying_MJPerson 類對象作為MJPerson類對象的子類對象,通過底層_NSSet**ValueAndNotify方法(這里是_NSSetIntValueAndNotify) 重寫了setAge:方法,進而實現KVO的監聽過程。

以下可理解為實現該過程的偽代碼

#import "MJPerson.h"
@interface NSKVONotifying_MJPerson : MJPerson
@end

#import "NSKVONotifying_MJPerson.h"
@implementation NSKVONotifying_MJPerson

- (void)setAge:(int)age
{
    _NSSetIntValueAndNotify();
}

// 偽代碼
void _NSSetIntValueAndNotify()
{
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}

- (void)didChangeValueForKey:(NSString *)key
{
    // 通知監聽器,某某屬性值發生了改變
    [oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}

監聽過程圖

KVO監聽過程圖.png

驗證過程在 我的參考文獻1進行了很詳細的說明

KVO監聽數組

iOS默認不支持KVO的形式來監聽數組的變化,當我們想要使用KVO監聽數組的狀態時,要做到兩點:
1、要將監聽的數組封裝到model里,不能監聽UIViewController里面的數組
2、不能這樣[_model.modelArray addObject]方法,需要這樣調用
[[_model mutableArrayValueForKey:@"modelArray"] addObject:str];

@interface ObserverModel : NSObject
@property (strong,nonatomic)NSMutableArray *dataArray;
@end

@implementation ObserverModel
-(NSMutableArray *)dataArray{
    if(!_dataArray){
        _dataArray = [NSMutableArray array];
    }
    return _dataArray;
}
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    self.observerModel = [[ObserverModel alloc] init];
    [self.observerModel addObserver:self forKeyPath:@"dataArray" options:NSKeyValueObservingOptionNew context:nil];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [[self.observerModel mutableArrayValueForKeyPath:@"dataArray"] addObject:@"haha"];
//    [[obserVer mutableArrayValueForKeyPath:@"dataArray"] removeObject:voiceContent];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"dataArray"]){
        NSLog(@"%@",change[@"new"]);
    }
}

-(void)dealloc{
    if (self.observerModel != nil) {
        [self.observerModel removeObserver:self forKeyPath:@"dataArray"];
    }
}

參考文檔
http://www.lxweimin.com/p/8a9cd3d1bed7
http://www.lxweimin.com/p/4eae365accc1
http://www.lxweimin.com/p/f27d060fc1f1

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 最近發現之前看的東西沒一會就忘記了,所以寫來好一些,也建議大家吧學到了記錄下來這樣加深印象,也可以幫助到別人。首先...
    _技術支持閱讀 938評論 1 1
  • 1.給私有的成員變量賦值(當然公有的也可以) 比如一個類有一個私有成員變量(在.m文件中),用KVC的方式然后可以...
    sunny12閱讀 424評論 0 1
  • 先聊聊 KVO 與 KVC 的區別吧:KVO是指鍵-值-觀察者模式, 鍵值監聽, 監聽一個對象屬性值的改變。KVO...
    smile麗語閱讀 341評論 1 3
  • Swift1> Swift和OC的區別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,135評論 1 32
  • KVO與KVC簡單理解 ///////////////////////////////////////我是該死的分...
    李某lkb閱讀 403評論 0 1