KVO、KVC實(shí)踐

Key Value Observing(KVO)鍵值觀察,它提供一種機(jī)制,當(dāng)指定的對(duì)象的屬性被修改后,則對(duì)象就會(huì)接受到通知。簡(jiǎn)單的說就是每次被觀察對(duì)象的指定屬性被修改后,KVO就會(huì)自動(dòng)通知相應(yīng)的觀察者了。

KVO 的行為是同步的,與所觀察的值發(fā)生變化在同一個(gè)線程上。

使用方法

系統(tǒng)框架已經(jīng)支持KVO,所以在使用的時(shí)候非常簡(jiǎn)單:

1、注冊(cè),指定被觀察者的屬性,

- (void)addObserver:(NSObject *)anObserver

? ? ? ? ? ? ? forKeyPath:(NSString *)keyPath

? ? ? ? ? ? ? ? ? ? options:(NSKeyValueObservingOptions)options

? ? ? ? ? ? ? ? ? ? context:(void *)context

2、實(shí)現(xiàn)回調(diào)方法

- (void)observeValueForKeyPath:(NSString *)keyPath

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?ofObject:(id)object

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? change:(NSDictionary *)change

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? context:(void *)context

3、移除觀察

- (void)removeObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath

使用場(chǎng)景

KVO常使用在這樣一種場(chǎng)景:

我們從一個(gè)列表界面點(diǎn)擊進(jìn)入詳情頁(yè)面,我們會(huì)把列表數(shù)據(jù)Model傳值給詳情頁(yè),在詳情頁(yè)有很多操作,比如:點(diǎn)贊,收藏、刪除等等。我們希望在詳情頁(yè)操作完,回到列表頁(yè)時(shí),對(duì)應(yīng)的狀態(tài):點(diǎn)贊數(shù)量,收藏?cái)?shù)量,刪除狀態(tài)等等也需要同時(shí)更新。

一般的操作,我們會(huì)發(fā)送通知,改變狀態(tài),或者回到列表頁(yè)面,再次請(qǐng)求接口。這兩種方式都不是很理想。

有了KVO,我們只需要觀察我們需要改變狀態(tài)的屬性,比如:點(diǎn)贊數(shù)量,收藏?cái)?shù)量,刪除狀態(tài)等等。在詳情頁(yè)面操作后,只需要把Model相應(yīng)的屬性改變,列表頁(yè)面也會(huì)同時(shí)更新。

示例:

1、創(chuàng)建一個(gè)要被觀察的Model

@interface DataModel : NSObject

? @property (strong, nonatomic) NSString* name;

? @property (assign, nonatomic) NSUInteger age;

@end


2、注冊(cè),指定被觀察者的屬性,

DataModel* data = [[DataModel alloc] init];

[data addObserver:self

? ? ? ? ? ? forKeyPath:@"name"

? ? ? ? ? ? ? ? ? options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld

? ? ? ? ? ? ? ? ? ?context:NULL];

3、實(shí)現(xiàn)回調(diào)方法

- (void)observeValueForKeyPath:(NSString*)keyPath

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ofObject:(id)object

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?change:(NSDictionary*)change

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?context:(void*)context

{

? ? if ([keyPath isEqualToString:@"name"]) {

? ? ? ? // 更新UI操作

}}

4、增加觀察與取消觀察是成對(duì)出現(xiàn)的,所以需要在最后的時(shí)候,移除觀察者

[data removeObserver:self forKeyPath:@"name"];

KVO基本原理:

1、KVO是基于Runtime機(jī)制實(shí)現(xiàn)的。

2、當(dāng)某個(gè)類的屬性對(duì)象第一次被觀察時(shí),系統(tǒng)就會(huì)在運(yùn)行期動(dòng)態(tài)地創(chuàng)建該類的一個(gè)派生類,在這個(gè)派生類中重寫基類中任何被觀察屬性的setter方法。派生類在被重寫的setter方法內(nèi)實(shí)現(xiàn)真正的通知機(jī)制。

3、如果原類為DataModel,那么生成的派生類名為NSKVONotifying_DataModel。

4、每個(gè)類對(duì)象中都有一個(gè)isa指針指向當(dāng)前類,當(dāng)一個(gè)類對(duì)象的第一次被觀察,那么系統(tǒng)會(huì)偷偷將isa指針指向動(dòng)態(tài)生成的派生類,從而在給被監(jiān)控屬性賦值時(shí)執(zhí)行的是派生類的setter方法。

5、鍵值觀察通知依賴于NSObject 的兩個(gè)方法: willChangeValueForKey: 和 didChangevlueForKey: 在一個(gè)被觀察屬性發(fā)生改變之前,willChangeValueForKey: 會(huì)被調(diào)用,會(huì)記錄舊的值。而當(dāng)改變發(fā)生后,didChangeValueForKey: 會(huì)被調(diào)用,繼而 observeValueForKey:ofObject:change:context: 也會(huì)被調(diào)用。


Key Value Coding(KVC)鍵值編碼,可以修改屬性的值,并且可以修改私有的成員比變量,可以取值。

通過對(duì)象的屬性名稱:訪問、設(shè)置對(duì)象屬性的值

1、創(chuàng)建對(duì)象

@interface DataModel : NSObject

? @property (strong, nonatomic) NSString* name;

? @property (assign, nonatomic) int age;

@end

2、給屬性設(shè)置值

DataModel* data = [[DataModel alloc] init];

[data setValue:@"KVC" forKey:@"name"];

[data setValue:@"23" forKey:@"age"];

3、訪問對(duì)象的值

NSLog(@"%@", [data valueForKey:@"name"]); // -> KVC

NSLog(@"%@", [data valueForKey:@"age"]); // -> 23

使用鍵路徑(Key Path):訪問、設(shè)置對(duì)象屬性的值

1、創(chuàng)建對(duì)象

@interface Person : NSObject

? @property (strong, nonatomic) NSString* address;

? @property (assign, nonatomic) int number;

@end

@interface DataModel : NSObject

? @property (strong, nonatomic) NSString* name;

? @property (assign, nonatomic) int age;

? @property (strong, nonatomic) Person* person;

@end

2、給屬性設(shè)置值

DataModel* data = [[DataModel alloc] init];

[data setValue:@"KVC" forKey:@"name"];

[data setValue:@"23" forKey:@"age"];

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

[person setValue:@"Bei Jing" forKey:@"address"];

[self.data setValue:person forKey:@"person"];

3、訪問對(duì)象的值

NSLog(@"%@", [data valueForKey:@"name"]);// -> KVC

NSLog(@"%@", [data valueForKey:@"age"]);// -> 23

// 這里用 valueForKeyPath 而不是 valueForKey

NSLog(@"%@", [data valueForKeyPath:@"person.address"]);// -> Bei Jing

訪問、設(shè)置集合的值

1、創(chuàng)建對(duì)象

@interface DataModel : NSObject

? @property (strong, nonatomic) NSArray* number;

? @property (strong, nonatomic) NSArray* personArray;

@end


2、賦值

DataModel* data = [[DataModel alloc] init];

[data setValue:@[@"11", @"23", @"32"] forKey:@"number"];

Person* person1 = [[Person alloc] init];

[person1 setValue:@"12" forKey:@"number"];

Person* person2 = [[Person alloc] init];

[person2 setValue:@"22" forKey:@"number"];

[self.data setValue:@[person1, person2] forKey:@"personArray"];

3、訪問對(duì)象的值

NSLog(@"%@", [data valueForKey:@"number"]);// -> (11, 23, 32)

NSLog(@"%@", [data valueForKey:@"personArray"]);// -> (person, person)

獲得數(shù)組所有元素的最大值

NSLog(@"%@", [self.data valueForKeyPath:@"number.@max.self"]); // -> 32

獲得數(shù)組所有元素的和

NSLog(@"%@", [self.datavalueForKeyPath:@"number.@sum.self"]);// -> 66

獲得數(shù)組所有元素的平均值

NSLog(@"%@", [self.datavalueForKeyPath:@"number.@avg.self"]);// -> 22

獲得數(shù)組所有元素的最小值

NSLog(@"%@", [self.datavalueForKeyPath:@"number.@min.self"]);// -> 11

同理:可以獲得person對(duì)象的number(數(shù)組屬性)元素的最大值、最小值、平均值以及和。以最大值為例:

NSLog(@"%@", [self.datavalueForKeyPath:@"personArray.@max.number"]);// -> 22

KVC基本原理:

當(dāng)一個(gè)對(duì)象調(diào)用setValue方法時(shí),方法內(nèi)部會(huì)做以下操作:

1、檢查是否存在相應(yīng)的key的setter方法,如果存在,就調(diào)用setter方法。

2、如果setter方法不存在,就會(huì)查找與key相同名稱并且?guī)聞澗€的成員變量,如果有,則直接給成員變量屬性賦值。

3、如果沒有找到_key,就會(huì)查找相同名稱的屬性key,如果有就直接賦值。

4、如果還沒有找到,則調(diào)用valueForUndefinedKey:和setValue:forUndefinedKey:方法。


KVO、NSNotification、Delegate以及Block區(qū)別

KVO是Cocoa框架實(shí)現(xiàn)的觀察者模式,通過KVO可以監(jiān)測(cè)一個(gè)對(duì)象屬性值的變化。是一對(duì)多的關(guān)系,一個(gè)屬性值的變化會(huì)通知所有的觀察者。

NSNotification是通知,也是一對(duì)多的關(guān)系。在某些情況下,KVO和NSNotification是一樣的,都是狀態(tài)變化之后告知觀察者。NSNotification需要被觀察者先主動(dòng)發(fā)出通知,然后觀察者才會(huì)進(jìn)行響應(yīng),比KVO多了發(fā)送通知的一步,但是其優(yōu)點(diǎn)是監(jiān)聽不局限于屬性的變化,還可以對(duì)多種多樣的狀態(tài)變化進(jìn)行監(jiān)聽,監(jiān)聽范圍廣,使用也更靈活。

Delegate 是代理,就是把自己的事情交給別人做。讓代理類完成所需要的操作。Delegate是一對(duì)一關(guān)系。

Block是Delegate的另一種形式,是函數(shù)式編程的一種形式。使用場(chǎng)景跟Delegate一樣,相比Delegate更靈活,而且代理的實(shí)現(xiàn)更直觀。

1、KVO一般的使用場(chǎng)景是數(shù)據(jù)(Model),需求是數(shù)據(jù)變化,比如股票價(jià)格變化,我們一般使用KVO(觀察者模式)。

2、Delegate一般的使用場(chǎng)景是行為,需求是需要?jiǎng)e人幫我做一件事情,比如買賣股票,我們一般使用Delegate。

3、NSNotification一般是進(jìn)行全局通知。

4、Delegate是強(qiáng)關(guān)聯(lián),就是委托和代理雙方互相知道,你委托別人買股票你就需要知道經(jīng)紀(jì)人,經(jīng)紀(jì)人也需要知道自己的顧客。NSNotification是弱關(guān)聯(lián),消息發(fā)出,你不需要知道是誰(shuí)發(fā)的消息,同理發(fā)消息的人也不需要知道接收消息的人。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 目錄:1.KVC用法;2.KVC和對(duì)象的setter、getter方法的區(qū)別;3.key和keyPath的區(qū)別;4...
    倫倫子_f7b3閱讀 686評(píng)論 0 1
  • KVC(Key-value coding)鍵值編碼,單看這個(gè)名字可能不太好理解。其實(shí)翻譯一下就很簡(jiǎn)單了,就是指iO...
    朽木自雕也閱讀 1,699評(píng)論 6 1
  • 設(shè)計(jì)模式是什么? 你知道哪些設(shè)計(jì)模式,并簡(jiǎn)要敘述? 設(shè)計(jì)模式是一種編碼經(jīng)驗(yàn),就是用比較成熟的邏輯去處理某一種類型的...
    琦均煞Sylar閱讀 512評(píng)論 0 0
  • 我是抱著很高的期待進(jìn)影院看這部片子的,花6億建成的唐城,各種宣傳中不停出現(xiàn)的“大唐盛世”這樣吸引眼球的字眼,都讓我...
    永恆之光閱讀 360評(píng)論 0 0
  • day134。 周末,6點(diǎn)自然醒。精神不錯(cuò),但意志力薄弱,一不小心回籠到8點(diǎn)。盡管臨近生理期,但覺得身體還不錯(cuò),特...
    飛天小毛女閱讀 197評(píng)論 0 0

友情鏈接更多精彩內(nèi)容