歡迎你們關注個人公衆號,我會按期分享一些我在項目中遇到問題的解決辦法和一些iOS實用的技巧,現階段主要是整理出一些基礎的知識記錄下來javascript
文章也會同步更新到個人博客:
ppsheep.comjava
KVO 行爲是同步的 而且發生與所觀察的值發生變化的一樣的線程上。這聽起來有點拗口,簡單點說,就是監聽行爲發生的線程和所觀察的值發生變化的線程,確定是同一個線程,這樣咱們使用的時候就須要注意了:數組
當咱們試圖從其餘線程改變屬性值的時候咱們應當十分當心,除非能肯定全部的觀察者都用線程安全的方法處理 KVO 通知安全
一般來講,咱們不推薦把 KVO 和多線程混起來。若是咱們要用多個隊列和線程,咱們不該該在它們互相之間用 KVO。多線程
最簡單的 KVC 能讓咱們經過如下的形式訪問屬性:框架
@property (nonatomic, copy) NSString *name;複製代碼
取值:ide
NSString *n = [object valueForKey:@"name"]複製代碼
設定:工具
[object setValue:@"Daniel" forKey:@"name"]複製代碼
這個不只能夠訪問做爲對象屬性,並且也能訪問一些標量(例如 int 和 CGFloat)和 struct(例如 CGRect)。Foundation 框架會爲咱們自動封裝它們。舉例來講,若是有如下屬性:atom
@property (nonatomic) CGFloat height;複製代碼
咱們能夠這樣設置:spa
[object setValue:@(20) forKey:@"height"]複製代碼
KVC 一樣容許咱們經過關係來訪問對象。假設 person 對象有屬性 address,address 有屬性 city,咱們能夠這樣經過 person 來訪問 city:
[person valueForKeyPath:@"address.city"]複製代碼
值得注意的是這裏咱們調用 -valueForKeyPath: 而不是 -valueForKey:
不須要 @property 的 KVC
咱們能夠實現一個支持 KVC 而不用 @property 和 @synthesize 或是自動 synthesize 的屬性。最直接的方式是添加 -
- (NSString *)name;
- (void)setName:(NSString *)name;複製代碼
這徹底等於 @property 的實現方式。
可是當標量和 struct 的值被傳入 nil 的時候尤爲須要注意。假設咱們要 height 屬性支持 KVC 咱們寫了如下的方法:
- (CGFloat)height;
- (void)setHeight:(CGFloat)height;複製代碼
而後咱們這樣調用:
[object setValue:nil forKey:@"height"]複製代碼
這會拋出一個 exception。要正確的處理 nil,咱們要像這樣 override -setNilValueForKey:
- (void)setNilValueForKey:(NSString *)key
{
if ([key isEqualToString:@"height"]) {
[self setValue:@0 forKey:key];
} else
[super setNilValueForKey:key];
}複製代碼
一個經常被忽視的 KVC 特性是它對集合操做的支持。舉個例子,咱們能夠這樣來得到一個數組中最大的值:
NSArray *a = @[@4, @84, @2];
NSLog(@"max = %@", [a valueForKeyPath:@"@max.self"]);複製代碼
或者說,咱們有一個 Transaction 對象的數組,對象有屬性 amount 的話,咱們能夠這樣得到最大的 amount:
NSArray *a = @[transaction1, transaction2, transaction3];
NSLog(@"max = %@", [a valueForKeyPath:@"@max.amount"]);複製代碼
當咱們調用 [a valueForKeyPath:@"@max.amount"] 的時候,它會在數組 a 的每一個元素中調用 -valueForKey:@"amount" 而後返回最大的那個。
首先,KVO 兼容是 API 的一部分。若是類的全部者不保證某個屬性兼容 KVO,咱們就不能保證 KVO 正常工做。蘋果文檔裏有 KVO 兼容屬性的文檔。例如,NSProgress 類的大多數屬性都是兼容 KVO 的。
當作出改變之後,有些人試着放空的 -willChange 和 -didChange 方法來強制 KVO 的觸發。KVO 通知雖然會生效,可是這樣作破壞了有依賴於 NSKeyValueObservingOld 選項的觀察者。詳細來講,這影響了 KVO 對觀察鍵路徑 (key path) 的原生支持。KVO 在觀察鍵路徑 (key path) 時依賴於 NSKeyValueObservingOld 屬性。
咱們也要指出有些集合是不能被觀察的。KVO 旨在觀察關係 (relationship) 而不是集合。咱們不能觀察 NSArray,咱們只能觀察一個對象的屬性——而這個屬性有多是 NSArray。舉例說,若是咱們有一個 ContactList 對象,咱們能夠觀察它的 contacts 屬性。可是咱們不能向要觀察對象的 -addObserver:forKeyPath:... 傳入一個 NSArray。
類似地,觀察 self 不是永遠都生效的。並且這不是一個好的設計。
你能夠在 lldb 裏查看一個被觀察對象的全部觀察信息。
(lldb) po [observedObject observationInfo]複製代碼
這會打印出有關誰觀察誰之類的不少信息。
這個信息的格式不是公開的,咱們不能讓任何東西依賴它,由於蘋果隨時均可以改變它。不過這是一個很強大的排錯工具。
參考: