KVC 和 KVO(三)

歡迎你們關注個人公衆號,我會按期分享一些我在項目中遇到問題的解決辦法和一些iOS實用的技巧,現階段主要是整理出一些基礎的知識記錄下來javascript



文章也會同步更新到個人博客:
ppsheep.comjava

KVO和線程

KVO 行爲是同步的 而且發生與所觀察的值發生變化的一樣的線程上。這聽起來有點拗口,簡單點說,就是監聽行爲發生的線程和所觀察的值發生變化的線程,確定是同一個線程,這樣咱們使用的時候就須要注意了:數組

當咱們試圖從其餘線程改變屬性值的時候咱們應當十分當心,除非能肯定全部的觀察者都用線程安全的方法處理 KVO 通知安全

一般來講,咱們不推薦把 KVO 和多線程混起來。若是咱們要用多個隊列和線程,咱們不該該在它們互相之間用 KVO。多線程

KVC

最簡單的 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"]複製代碼

鍵路徑 (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 的屬性。最直接的方式是添加 - 和 -set : 方法。例如咱們想要 name ,咱們這樣作:

- (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]複製代碼

這會打印出有關誰觀察誰之類的不少信息。

這個信息的格式不是公開的,咱們不能讓任何東西依賴它,由於蘋果隨時均可以改變它。不過這是一個很強大的排錯工具。

參考:

www.objccn.io/issue-7-3/

相關文章
相關標籤/搜索