歡迎大家關注我的公眾號,我會定期分享一些我在專案中遇到問題的解決辦法和一些iOS實用的技巧,現階段主要是整理出一些基礎的知識記錄下來
文章也會同步更新到我的部落格:
ppsheep.com
KVO和執行緒
KVO 行為是同步的 並且發生與所觀察的值發生變化的同樣的執行緒上。這聽起來有點拗口,簡單點說,就是監聽行為發生的執行緒和所觀察的值發生變化的執行緒,肯定是同一個執行緒,這樣我們使用的時候就需要注意了:
當我們試圖從其他執行緒改變屬性值的時候我們應當十分小心,除非能確定所有的觀察者都用執行緒安全的方法處理 KVO 通知
通常來說,我們不推薦把 KVO 和多執行緒混起來。如果我們要用多個佇列和執行緒,我們不應該在它們互相之間用 KVO。
KVC
最簡單的 KVC 能讓我們通過以下的形式訪問屬性:
@property (nonatomic, copy) NSString *name;複製程式碼
取值:
NSString *n = [object valueForKey:@"name"]複製程式碼
設定:
[object setValue:@"Daniel" forKey:@"name"]複製程式碼
這個不僅可以訪問作為物件屬性,而且也能訪問一些標量(例如 int 和 CGFloat)和 struct(例如 CGRect)。Foundation 框架會為我們自動封裝它們。舉例來說,如果有以下屬性:
@property (nonatomic) CGFloat height;複製程式碼
我們可以這樣設定:
[object setValue:@(20) forKey:@"height"]複製程式碼
鍵路徑 (key path)
KVC 同樣允許我們通過關係來訪問物件。假設 person 物件有屬性 address,address 有屬性 city,我們可以這樣通過 person 來訪問 city:
[person valueForKeyPath:@"address.city"]複製程式碼
值得注意的是這裡我們呼叫 -valueForKeyPath: 而不是 -valueForKey:
Key-Value Coding Without @property
不需要 @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 錯誤
首先,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 不是永遠都生效的。而且這不是一個好的設計。
除錯 KVO
你可以在 lldb 裡檢視一個被觀察物件的所有觀察資訊。
(lldb) po [observedObject observationInfo]複製程式碼
這會列印出有關誰觀察誰之類的很多資訊。
這個資訊的格式不是公開的,我們不能讓任何東西依賴它,因為蘋果隨時都可以改變它。不過這是一個很強大的排錯工具。
參考: