一、鍵值編碼KVC
kvc&kvo視頻講解
1、介紹
由于oc的語言特性,使得開發者根本不必進行任何操作就可以進行屬性的動態讀寫,這種方式就是Key Value Coding(簡稱KVC)。
KVC的操作方法由NSKeyValueCoding協議提供,而NSObject就實現了這個協議,也就是說OC中幾乎所有的對象都支持KVC操作,常用的KVC操作方法如下:
- 動態設置:
setValue:屬性值 forKey:屬性名
用于簡單路徑;setValue:屬性值 forKeyPath:屬性路徑
用于復合路徑,例如Person有一個Account類型的屬性,那么person.account就是一個復合屬性 - 動態讀取:
valueForKey:屬性名
、valueForKeyPath:屬性名
2、一個簡單的栗子??!!
創建一個OC項目,創建一個Account
類。在該類中,聲明屬性balance
// Account.h
// KVC&KVO
#import <Foundation/Foundation.h>
@interface Account : NSObject
@property(nonatomic,assign) float balance;
@end
新建一個Person
類。在該類中,聲明一個私有屬性age
,聲明公開屬性name
和account
。聲明公開方法showMessage
展示用戶信息。并到Person.m
文件中實現該方法。
// Person.h
#import <Foundation/Foundation.h>
#import "Account.h"
@interface Person : NSObject{
@private int _age;
}
@property (nonatomic,copy) NSString *name;
@property (nonatomic,retain) Account *account;
//公共方法
- (void)showMessage;
@end
// Person.m
#import "Person.h"
@implementation Person
- (void)showMessage {
NSLog(@"name = %@,age = %d",_name,_age);
}
@end
在main函數中,使用KVC的方法對以上類的屬性進行賦值和取值。
int main(int argc, char * argv[]) {
@autoreleasepool {
Person *person1 = [[Person alloc] init];
// 私有變量仍然可以訪問
[person1 setValue:@28 forKey:@"age"];
[person1 setValue:@"llx" forKey:@"name"];
[person1 showMessage];
NSLog(@"person1's name is :%@,age is :%@",person1.name,[person1 valueForKey:@"age"]);
Account *account = [[Account alloc] init];
// 注意這一步一定要先給account屬性賦值,否則下面按路徑賦值無法成功,因為account為nil,當然這一步驟也可以寫成:[person1 setValue:account1 forKeyPath:@"account"];
person1.account = account;
[person1 setValue:@100000000.0 forKeyPath:@"account.balance"];
NSLog(@"person1's balance is :%.2f",[[person1 valueForKeyPath:@"account.balance"] floatValue]);
}
return 0;
}
3、KVC讀取屬性的查找規則
假設現在要利用KVC對a屬性進行讀取。
如果是動態設置屬性,則優先考慮調用setA
方法。如果沒有該方法則優先考慮搜索成員變量_a
,如果仍然不存在則搜索成員變量a
,如果最后仍然沒有搜索到這會調用這個類的setValue:forUndefinedKey:
方法。在搜索過程中,不管這些方法、成員變量是私有還是公共的都能正確設置。
如果是動態讀取屬性,則優先調用a
的getter
方法,如果沒有搜索到則會優先搜索成員變量_a
,如果仍然不存在則會搜索成員變量a,如果仍然沒搜索到就會調用這個類的valueforUndefinedKey:
方法。而且,在搜索過程中,不管這些方法、成員變量是私有的還是公有的都能正確讀取。
二、鍵值監聽KVO
1、介紹
在OC中支持一種雙向綁定機制,如果數據模型修改了之后會立即反映到UI視圖上,這種機制在OC中被稱為Key value Observing
。KVO其實是一種觀察者模式,利用它可以很容易實現視圖組件和數據模型的分離,當數據模型的屬性值改變之后作為監聽器的視圖組件就會被激發,激發時就會回調監聽器自身。在OC中要實現KVO則必須要實現NSKeyValueObServing
協議,不過幸運的是NSObject
已經實現了該協議,因此幾乎所有的OC對象都能使用KVO。
在OC中使用KVO操作的常用方法如下:
- 注冊指定Key路徑的監聽器:
addObserver:forKeyPath:options:context
- 刪除指定Key路徑的監聽器:
removeObserver:forKeyPath
、removeObserver:forKeyPath:context:
- 回調監聽:
observeValueForKeyPath:ofObject: change:context:
2、KVO的使用步驟
- 通過
addObserver:forKeyPath:options:context
為被監聽的對象(通常是數據模型)注冊監聽器 - 重寫監聽器的
observeValueForKeyPath:ofObject:change:context:
方法
3、一個簡單的栗子??!!
在上面KVC的案例上繼續擴展。當賬戶余額balance
變動之后用戶總會希望可以及時獲得通知的。那么此時就將account
作為監聽對象,需要Person
為它注冊監聽。使用addObserver: forKeyPath: options: context:
而Person
作為監聽器需要重寫它的observeValueForKeyPath: ofObject: change: context:
方法,當監聽的余額發生改變后會回調監聽器方法observeValueForKeyPath: ofObject: change: context:
。
下面通過代碼模擬上面的過程:在Person.m文件中添加方法
- 在setAccount的方法中添加對Account的監聽
- (void)setAccount:(Account *)account {
_account = account;
// 添加對Account的監聽
[self.account addObserver:self forKeyPath:@"balance" options:NSKeyValueObservingOptionNew context:nil];
}
- 重寫observeValueForKeyPath方法,設計當監聽的屬性變化之時要做的操作
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if([keyPath isEqualToString:@"balance"]){
NSLog(@"keyPath=%@,object=%@,newValue=%.2f,context=%@",keyPath,object,[[change objectForKey:@"new"] floatValue],context);
}}
- 重寫銷毀方法,移除監聽
-(void)dealloc{
[self.account removeObserver:self forKeyPath:@"balance"]; //移除監聽
}
在這段代碼中給balance
屬性添加了監聽,并且在監聽回調方法中輸出了監聽到的信息,最后在對象銷毀的時候移除監聽。這就構成了一個典型的KVO應用。
三、面試題
1、什么是KVC,說說它的優缺點?
答:KVC是一種不需要調用存取方法,就能直接通過實例變量訪問對象屬性的機制。很多情況下會簡化程序代碼。
但由于KVC不會對鍵和鍵路徑進行錯誤檢查,所以編譯器無法檢測錯誤。而且使用KVC后的執行效率要低于合成存取器,因為使用KVC必須先解析字符串,然后再設置或服務對象的實例變量。
2、NSNotification和KVO的區別和用法是什么?什么時候應該使用通知,什么時候應該使用KVO,它們的實現上有什么區別嗎?
答:NSNotification是通知模式在iOS的實現,KVO的全稱是鍵值觀察(Key-value observing),其是基于KVC(key-value coding)的,KVC是一個通過屬性名訪問屬性變量的機制。將Model層的變化,通知到多個Controller對象時,可以使用NSNotification;如果是只需要觀察某個對象的某個屬性,可以使用KVO。
3、如何關閉默認的KVO的默認實現,KVO的實現原理?
答:所謂的“手動觸發”是區別于“自動觸發”:
自動觸發是指類似這種場景:在注冊 KVO 之前設置一個初始值,注冊之后,設置一個不一樣的值,就可以觸發了。
想知道如何手動觸發,必須知道自動觸發 KVO 的原理:
鍵值觀察通知依賴于 NSObject 的兩個方法: willChangeValueForKey:
和didChangevlueForKey:
。在一個被觀察屬性發生改變之前, willChangeValueForKey: 一定會被調用,這就 會記錄舊的值。而當改變發生后, didChangeValueForKey:
會被調用,繼而 observeValueForKey:ofObject:change:context:
也會被調用。如果可以手動實現這些調用,就可以實現“手動觸發”了。