KVC和KVO

目錄:

1.KVC用法;

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

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

4.KVC進(jìn)行求和剃法,求平均值等操作碎捺;

5.KVO的用法;

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

一.KVC


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

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

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

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

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

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

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



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


一般情況下钦椭,KVC和setter拧额、getter應(yīng)該說都能達(dá)到對對象屬性的賦值碑诉,并且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去可以實(shí)現(xiàn)賦值署照、取值

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




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


keyPath方法是集成了key的所有功能祸泪,也就是說對一個對象的一般屬性進(jìn)行賦值、取值建芙,兩個方法是通用的没隘,都可以實(shí)現(xiàn)。但是對對象中的對象進(jìn)的屬性行賦值禁荸,只有keyPath能夠?qū)崿F(xiàn)右蒲。

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

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

if (self = [super init]) {

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

}

???????? return self;

}




4.KVC進(jìn)行求和,求平均值等操作


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)聽其值得改變赶熟。用法如下:

添加觀察者

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

移除觀察者

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

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

/* NSKeyValueObservingOptionNew =0x01, 新值

* NSKeyValueObservingOptionOld =0x02, 舊值

*/

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

a.name = @"zzz";

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

/* * 當(dāng)對象的屬性發(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指針,會在隨后的簡述中介紹)

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

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


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

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

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





2.根據(jù)KVO底層原理自己實(shí)現(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);

}

//點(diǎn)擊就改變!!

- (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)系作者
  • 序言:七十年代末蜻韭,一起剝皮案震驚了整個濱河市悼尾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肖方,老刑警劉巖闺魏,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異窥妇,居然都是意外死亡舷胜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門活翩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來烹骨,“玉大人,你說我怎么就攤上這事材泄【诨溃” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵拉宗,是天一觀的道長峦树。 經(jīng)常有香客問我,道長旦事,這世上最難降的妖魔是什么魁巩? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮姐浮,結(jié)果婚禮上谷遂,老公的妹妹穿的比我還像新娘。我一直安慰自己卖鲤,他們只是感情好肾扰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布畴嘶。 她就那樣靜靜地躺著,像睡著了一般集晚。 火紅的嫁衣襯著肌膚如雪窗悯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天偷拔,我揣著相機(jī)與錄音蒋院,去河邊找鬼。 笑死条摸,一個胖子當(dāng)著我的面吹牛悦污,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钉蒲,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼切端,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了顷啼?” 一聲冷哼從身側(cè)響起踏枣,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钙蒙,沒想到半個月后茵瀑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躬厌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年马昨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扛施。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸿捧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出疙渣,到底是詐尸還是另有隱情匙奴,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布妄荔,位于F島的核電站泼菌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏啦租。R本人自食惡果不足惜哗伯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望篷角。 院中可真熱鬧焊刹,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阱缓。三九已至非凌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荆针,已是汗流浹背敞嗡。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留航背,地道東北人喉悴。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像玖媚,于是被迫代替她去往敵國和親箕肃。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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

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