今天趁着項目bug修復完了,來說解一下OC知識的另外一個技術點-KVC!針對KVC,講解兩個知識點atom
1、問:經過KVC修改屬性會觸發KVO麼?spa
答:會觸發KVO3d
建立工程項目TestKVO,ZXYPerson類有一個屬性age,在控制器ViewController中添加屬性觀察者KVO,項目代碼以下code
@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方式更改屬性的值,將上面代碼運行結果以下:server
經過上面發現setValue:forKeyPath觸發了KVO,同理發現setValue:forKey也會觸發KVO,可是這兩個方法有什麼區別呢?blog
setValue:forKeyPath會一層一層的(沿着路徑)向下找,然而setValue:forKey並不會這樣!(假如ZXYPerson養了一隻貓,貓有age屬性 ,經過"_p.cat.age"設置應該用setValue:forKeyPath,不能用setValue:forKey)rem
思考: 爲何KVC更改屬性值會觸發KVO?那就須要講解下面知識。get
2、問:KVC的賦值過程是怎樣的?原理是什麼?博客
setValue:forKey:的原理it
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如何取值?
3、問: 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的基本內容,但願對你們有所幫助,能夠關注博客會實時更新,謝謝!!!