KVC講解

今天趁着項目bug修復完了,來講解一下OC知識的另一個技術點-KVC!針對KVC,講解兩個知識點

  • 通過KVC修改屬性會觸發KVO么?
  • KVC的賦值過程是怎樣的?原理是什麼?
  • KVC的取值過程是怎樣的?原理是什麼?

 

一、問:通過KVC修改屬性會觸發KVO么?

答:會觸發KVO

創建工程項目TestKVO,ZXYPerson類有一個屬性age,在控制器ViewController中添加屬性觀察者KVO,項目代碼如下

@interface ViewController ()
@property(nonatomic,strong) ZXYPerson
*p; @end

@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; _p = [[ZXYPerson alloc]init]; _p.age = 10; [_p addObserver:self forKeyPath:@"age" options: NSKeyValueObservingOptionNew context:nil]; [_p setValue:@12 forKeyPath: @"age"]; } -(void)dealloc { [_p removeObserver:self forKeyPath:@"age"]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ NSLog(@"*********%@", change); }

上面橙色文字通過KVC方式更改屬性的值,將上面代碼運行結果如下:

 

通過上面發現setValue:forKeyPath觸發了KVO,同理髮現setValue:forKey也會觸發KVO,但是這兩個方法有什麼區別呢?

setValue:forKeyPath會一層一層的(沿着路徑)向下找,然而setValue:forKey並不會這樣!(假如ZXYPerson養了一隻貓,貓有age屬性 ,通過”_p.cat.age”設置應該用setValue:forKeyPath,不能用setValue:forKey

思考: 為什麼KVC更改屬性值會觸發KVO?那就需要講解下面知識。

 

二、問:KVC的賦值過程是怎樣的?原理是什麼?

 setValue:forKey:的原理

accessInstanceVariablesDirectly方法的默認返回值是YES

下面一一驗證上面的順序:

 驗證setValue:forkey調用過程不需要用到KVO,去除多餘的代碼之後,簡化成如下:

#import "ViewController.h"  #import "ZXYPerson.h"    @interface ViewController ()    @property(nonatomic,strong) ZXYPerson *p;  @end    @implementation ViewController  - (void)viewDidLoad {      [super viewDidLoad];        _p = [[ZXYPerson alloc]init];      [_p setValue:@12 forKeyPath: @"age"];    }    @end      #import "ZXYPerson.h"    @implementation ZXYPerson    - (void) setAge:(int)age {      NSLog(@"調用了setAge方法");  }    - (void) _setAge: (int)age {      NSLog(@"調用了_setAge方法");  }    @end

去除了age屬性的聲明,看看KVC賦值的前期過程(按照setKey, _setKey方法走)

 同時寫了兩個方法,優先調用setAge方法,假如將setAge方法注釋掉

 注釋掉setAge方法後,久調用了_setAge方法,證實了KVC的前期賦值情況!

 

如果兩個方法都沒有實現,此時KVC會accessInstanceVariablesDirectly方法,返回Yes代表可以直接訪問成員變量,反之不能訪問成員變量!

如果返回為Yes,會按照_key、_isKey、key、isKey成員屬性進行賦值

此時像上面的代碼加入這四個成員變量,如下(前提accessInstanceVariablesDirectly方法返回Yes)

@interface ZXYPerson : NSObject  {      @public      int _age;      int _isAge;      int age;      int isAge;  }    @end

加入上述代碼,運行

 首先給_age賦值,當四個成員變量同時出現,假如將int _age成員變量注釋掉,如下:

發現當_age注釋掉之後,優先給_isAge賦值,優先級僅次於_age,假如將_isAge注釋掉之後

 發現給age賦值,同理將age成員變量注釋掉之後

 最後給isAge賦值,符合了上述setValue:forkey的訪問屬性的優先級 _key > _isKey > key > isKey的順序

 

如果這四個成員變量都沒有了,就會報異常

 通過上面講述知道setValue:forKey會觸發KVO

[_p setValue:@12 forKeyPath: @”age”]內部調用相當於

[p willChangeValueForKey @”age”]

p->_age = 12;

[p didChangeValueForKey @”age”]

所以會觸發KVO

以上就是setValue:forKey的賦值所有過程,希望大家再看看上述圖,下面講述KVC如何取值?

 

三、問: KVC的取值過程是怎樣的?原理是什麼?

valueForKey:的原理

 

 下面一一驗證上面的順序:

@interface ZXYPerson : NSObject{      @public      int _age;  }    @implementation ZXYPerson    - (int)getAge {      return 11;  }    - (int)age {      return 12;  }    - (int)isAge {      return 13;  }    - (int)_age {      return 14;  }    @end      @interface ViewController ()  @property(nonatomic,strong) ZXYPerson *p;  @end  @implementation ViewController  - (void)viewDidLoad {      [super viewDidLoad];        _p = [[ZXYPerson alloc]init];      _p->_age = 10;        NSLog(@"******%@",[_p valueForKey:@"age"]);    }    @end

看看KVC取值的前期過程(按照getAge > age > isAge > _age 方法走)

 

當有四個方法時,會優先調用getAge方法,如上面一樣打印出11,調用了getAge方法!假如把getAge()方法注釋掉,運行代碼:

 

 將getAge()方法注釋掉後,調用了age方法,驗證了getAge > age !假如把age方法注釋掉

 

將getAge()和age()方法注釋掉後,調用了isAge()方法,驗證了getAge > age > isAge !假如把isAge()方法注釋掉 

 

將getAge()和age()方法以及isAge()注釋掉後,調用了_age()方法,驗證了getAge > age > isAge > _age! 

如果四個方法都沒有實現,此時KVC會看accessInstanceVariablesDirectly方法,返回Yes代表可以直接查找成員變量,反之不能查找成員變量!

如果返回為Yes,會按照_key、_isKey、key、isKey成員屬性順序查找成員變量

此時像上面的代碼加入這四個成員變量,如下(前提accessInstanceVariablesDirectly方法返回Yes,去除四個方法)

    @public      int _age;      int _isAge;      int age;      int isAge;

加入了四個成員變量,控制器ViewController加入設置屬性的四個值的

    _p->_age = 11;      _p->_isAge = 12;      _p->age = 13;      _p->isAge = 14;

觀察成員變量的查找順序!驗證_key、_isKey、key、isKey

 

 ZXYPerson有四個成員變量,當向著上面代碼書寫,運行代碼結論是11,對應着_age這個成員變量,所以優先取值_age!當將 _age成員變量注釋掉以及賦值注釋掉後

 

發現運行結果為12,對應的結果時_isAge, 得出結論 _age > _isAge! 繼續將_isAge成員變量注釋掉以及賦值_isAge如下:

 

 發現運行結果為13,對應的結果時age, 得出結論 _age > _isAge > age! 繼續將age成員變量注釋掉以及賦值age如下:

 

 發現運行結果為14,對應的結果時isAge, 得出結論 _age > _isAge > age > isAge! 繼續將isAge成員變量注釋掉以及賦值isAge如下:

 如果都注釋掉,會報異常valueForUndefinedKey錯誤!

以上就是valueForKey的取值所有過程,希望大家再看看上述圖回顧KVC如何取值!

 

上述就是KVC的基本內容,希望對大家有所幫助,可以關注博客會實時更新,謝謝!!!