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ā)消息的人也不需要知道接收消息的人。