KVC和KVO

目錄:

1.KVC用法;

2.KVC和對象的setter、getter方法的區(qū)別;

3.key和keyPath的區(qū)別;

4.KVC進行求和,求平均值等操作;

5.KVO的用法;

6.根據(jù)KVO底層原理自己實現(xiàn)KVO

一.KVC


1.KVC用法(很簡單,不詳細介紹)

KVC也就是key-value-coding(鍵值編碼),簡而言之就是通過key值去進行賦值和取值。主要是是操作對象的屬性。以下是幾個常用的方法:

setValue:forKey:(為對象的屬性賦值)

setValue: forKeyPath:(為對象的屬性賦值(包含了setValue:forKey:的功能,并且還可以對對象內(nèi)的類的屬性進行賦值))

valueForKey:(根據(jù)key取值)

valueForKeyPath:(根據(jù)keyPath取值)

setValuesForKeysWithDictionary:(對模型進行一次性賦值)



2.KVC和對象的setter、getter方法的區(qū)別


一般情況下,KVC和setter、getter應該說都能達到對對象屬性的賦值,并且KVC操作也是去調(diào)用的setter方法和getter方法(針對一些已經(jīng)在.h中聲明的屬性而言)。但是對于一些私有屬性,那么這個時候setter、getter方法就沒有用了,這個時候KVC卻能發(fā)揮重要優(yōu)勢。

例如:在Person.m中

#import "Person.h"

@implementation Person

{

??????????? NSInteger _height;

}

@end

此時你會發(fā)現(xiàn)setter、getter已經(jīng)無能為力了,但是KVC去可以實現(xiàn)賦值、取值

[p setValue:@170 forKey:@"height"];




3.key和keyPath的區(qū)別


keyPath方法是集成了key的所有功能,也就是說對一個對象的一般屬性進行賦值、取值,兩個方法是通用的,都可以實現(xiàn)。但是對對象中的對象進的屬性行賦值,只有keyPath能夠?qū)崿F(xiàn)。

setValuesForKeysWithDictionary:的巧妙使用(字典轉(zhuǎn)模型)

-(instancetype)initWithDict:(NSDictionary *)dict{

if (self = [super init]) {

??????????? [self setValuesForKeysWithDictionary:dict];

}

???????? return self;

}




4.KVC進行求和,求平均值等操作


Person.h

#import?"Father.h"

#import?"Book.h"

@interface?Person?:?NSObject?{

@public

?????????? NSString?*_fullName;

@private

????????? NSString?*_name;

????????? Father?*_father;

????????? NSArray?*_books;

}

@end


Father.h

@interface?Father?:?NSObject?{

@protected

??????? NSString?*_name;

}

@end


Book.h

#import?

@interface?Book?:?NSObject?{

@private

??????????? NSString?*_name;

??????????? float?_price;

}

@end

使用代碼:

#import?

#import?"Person.h"

int?main(int?argc,?const?char?*?argv[])

{

???????????? @autoreleasepool?{

???????????????????? Person?*person?=?[[Person?alloc]?init];

??????????????????? //直接訪問public變量

?????????????????? person->_fullName?=?@"ALI?TOM";

?????????????????? NSLog(@"_fullName?:%@",person->_fullName);

?????????????? ?? //KVC方式

?????????????? ? ? [person?setValue:@"TOM"?forKey:@"_name"];

???????????????? ? NSLog(@"_name?:%@",?[person?valueForKey:@"_name"]);


?????????????????? Father *father = [[Father alloc] init];

?? ? ? ? ? ? ? ? ? [father?setValue:@"JACK"?forKey:@"_name"];

?????????????????? [person?setValue:father?forKey:@"_father"];

????????????????? //KVC路徑訪問

????????????????? NSLog(@"father.name?:%@",?[person?valueForKeyPath:@"_father._name"]);

????????????????? [person?setValue:@"JERRY"?forKeyPath:@"_father._name"];

?????????????????? NSLog(@"father.name?:%@",?[person?valueForKeyPath:@"_father._name"]);

????????????????? NSMutableArray *bookArray = [NSMutableArray arrayWithCapacity:3];

????????????? ? ? for?(int?i=0;?i<3;?i++)?{

???????????????????????? Book?*book?=?[[Book?alloc]?init];

???????????????????????? NSString?*bookName?=?[NSString?stringWithFormat:@"book%d",?i];

???????????????????????? [book?setValue:bookName?forKey:@"_name"];

???????????????????????? [book?setValue:@((i?+?1)??*?10.2)?forKey:@"_price"];

???????????????????????? [bookArray?addObject:book];

????????????????????????? [book?release];

? ? ? ? ? ? ? ? ? }

???????????????? [person setValue:bookArray forKey:@"_books"];

? ? ? ? ? ? ? ? ? ? //KVC計算

?????????????????? //通過@count獲取集合book個數(shù)

?????????????????? NSNumber?*bookCount?=?[person?valueForKeyPath:@"_books.@count"];

?????????????????? NSLog(@"book?count?:%@",?bookCount);

?????????????????? //價格總和

?????????????????? NSNumber?*sum?=?[person?valueForKeyPath:@"_books.@sum._price"];

?????????????????? NSLog(@"sum?:%@",?sum);

?????????????????? //價格平均值

?????????????????? NSNumber?*avg?=?[person?valueForKeyPath:@"_books.@avg._price"];

?????????????????? NSLog(@"vag?:%@",?avg);

????????????????? //最低價格

???????????????? NSNumber?*min?=?[person?valueForKeyPath:@"_books.@min._price"];

???????????????? NSLog(@"min?:%@",?min);

???????????????? //最高價格

???????????????? NSNumber?*max?=?[person?valueForKeyPath:@"_books.@max._price"];

???????????????? NSLog(@"max?:%@",?max);






二.KVO


1.KVO的用法

KVO也就是key-value-observing(即鍵值觀察),利用一個key來找到某個屬性并監(jiān)聽其值得改變。用法如下:

添加觀察者

在觀察者中實現(xiàn)監(jiān)聽方法,observeValueForKeyPath: ofObject: change: context:(通過查閱文檔可以知道,絕大多數(shù)對象都有這個方法,因為這個方法屬于NSObject)

移除觀察者

//讓對象b監(jiān)聽對象a的name屬性

//options屬性可以選擇是哪個

/* NSKeyValueObservingOptionNew =0x01, 新值

* NSKeyValueObservingOptionOld =0x02, 舊值

*/

[a addObserver:b forKeyPath:@"name"options:kNilOptionscontext:nil];

a.name = @"zzz";

#pragma mark - 實現(xiàn)KVO回調(diào)方法

/* * 當對象的屬性發(fā)生改變會調(diào)用該方法

* @param keyPath 監(jiān)聽的屬性

* @param object 監(jiān)聽的對象

* @param change 新值和舊值

* @param context 額外的數(shù)據(jù)

*/

- (void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)objectchange:(NSDictionary*)change context:(void *)context{

????????????? NSLog(@"%@的值改變了,",keyPath);

? ? ? ? ? ?? NSLog(@"change:%@", change);

}

//最后不要忘記了,去移除observer

- (void)dealloc{

??????????? [a removeObserver:b forKeyPath:@"name"];

}

KVO底層(這部分涉及到了runtime,關(guān)于isa指針,會在隨后的簡述中介紹)

當一個類的屬性被觀察的時候,系統(tǒng)會通過runtime動態(tài)的創(chuàng)建一個該類的派生類,并且會在這個類中重寫基類被觀察的屬性的setter方法,而且系統(tǒng)將這個類的isa指針指向了派生類,從而實現(xiàn)了給監(jiān)聽的屬性賦值時調(diào)用的是派生類的setter方法。重寫的setter方法會在調(diào)用原setter方法前后,通知觀察對象值得改變。

具體實現(xiàn)圖如下


1.當某個類的對象第一次被觀察時,系統(tǒng)就會在運行期動態(tài)地創(chuàng)建該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的 setter 方法。

2.派生類在被重寫的 setter 方法中實現(xiàn)真正的通知機制,就如前面手動實現(xiàn)鍵值觀察那樣。這么做是基于設(shè)置屬性會調(diào)用 setter 方法,而通過重寫就獲得了 KVO 需要的通知機制。當然前提是要通過遵循 KVO 的屬性設(shè)置方式來變更屬性值,如果僅是直接修改屬性對應的成員變量,是無法實現(xiàn) KVO 的。

3.同時派生類還重寫了 class 方法以“欺騙”外部調(diào)用者它就是起初的那個類。然后系統(tǒng)將這個對象的 isa 指針指向這個新誕生的派生類,因此這個對象就成為該派生類的對象了,因而在該對象上對 setter 的調(diào)用就會調(diào)用重寫的 setter,從而激活鍵值通知機制。此外,派生類還重寫了 dealloc 方法來釋放資源。





2.根據(jù)KVO底層原理自己實現(xiàn)KVO

#import "NSObject+HKKVO.h"

#import@implementation NSObject (QLKVO) //給NSObject增加分類

//自定義的KVO

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

{

?????????? //1.動態(tài)生成一個類!!

????????? //1.1獲取類名

????????? NSString * oldClassName = NSStringFromClass([self class]);

????????? NSString * newClassName = [@"QLKVO_" stringByAppendingString:oldClassName];

????????? const char * name = [newClassName UTF8String];

???????? //動態(tài)創(chuàng)建一個子類

????????? Class MyClass = objc_allocateClassPair([self class], name, 0);

????????? //添加方法

???????? class_addMethod(MyClass, @selector(setName:), (IMP)setName, "v@:@");

????????? //注冊類

??????? objc_registerClassPair(MyClass);

? ? ? ? //NSLog(@"%@", [self class]);? 會輸出:Person

?????? ? //修改isa,修改完后self變成了子類

???????? object_setClass(self, MyClass);

? ? ?? //NSLog(@"%@", [self class]);? 會輸出:hkKVO_Person

??????? //保存觀察者對象,這里的self指的是子類

????? ? objc_setAssociatedObject(self, @"objc", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}



void setName(id self,SEL _cmd,NSString * newName){

???????? NSLog(@"我監(jiān)聽到了!!");

? ? ? ?? id class = [self class];

??????? //拿到觀察者

??????? id objc = objc_getAssociatedObject(self, @"objc");

??????? //改變self的isa指針,指向父類

?????? object_setClass(self, class_getSuperclass(class));

?????? //調(diào)用父類的set方法!!

?????? objc_msgSend(self, @selector(setName:),newName);

???? ?? //? ? NSLog(@"修改完畢!!");

?

?????? //通知觀察者

????? ? objc_msgSend(objc, @selector(observeValueForKeyPath:ofObject:change:context:),self,@"name",nil,nil);

??????? //改回子類類型(如果不改,self就指向了父類,下次父類的name屬性更改的,就不會調(diào)用到這個函數(shù)里面去)

???????? object_setClass(self, class);

}



#import "NSObject+HKKVO.h"

@interface ViewController ()

/**? */@property(nonatomic,strong)Person * p;

@end@implementation ViewController

- (void)viewDidLoad {??

???????? [super viewDidLoad];? ?

?????? ? Person * p = [[Person alloc]init];

? ? ? ? ?? _p = p;??

?????????? //使用自定義的KVO來監(jiān)聽!Person 的 name 屬性?

? ? ? ? ? [p hk_addObserver:self forKeyPath:@"name" options:0 context:nil];??

?????????? NSLog(@"%@",[p class]);??

}

//監(jiān)聽到了就來了!!

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

????????? NSLog(@"哥么來了!!!%@",_p.name);

}

//點擊就改變!!

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event {

??????? static int i = 0;

??????? i++;

?????? _p.name = [NSString stringWithFormat:@"%d",i];

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內(nèi)容

  • 概念 先來看看概念,Key-value coding (KVC) 和 key-value observing (K...
    wuwy閱讀 1,355評論 0 1
  • 在iOS開發(fā)中,我們常常用到鍵值編碼KVC和鍵值監(jiān)聽KVO兩個東東,今天小編和大家分享的就是這兩個東東在應用開發(fā)中...
    突然自我閱讀 1,006評論 2 3
  • 在編程中,最常見的就是程序的流程取決于你所使用的各種變量和屬性的值,根據(jù)變量和屬性的值確定后面運行的代碼,有時會檢...
    pro648閱讀 1,652評論 2 27
  • KVC和KVO都屬于鍵值編程而且底層實現(xiàn)機制都是isa-swizzing 一.KVC概述 1.kvc 是一種通過(...
    俊俊吖閱讀 148評論 0 0
  • 遍歷遇到左括號一律進棧,右括號與棧頂?shù)臄?shù)比較,注意判斷棧不能為空,另外返回時也要檢查棧是否空
    Genejing閱讀 243評論 0 0