kvc kvo 總結---180313

==================================================kvc========kvc======================================================================================================

 

KVC(Key-value coding)鍵值編碼,單看這個名字可能不太好理解。其實翻譯一下就很簡單了,就是指iOS的開發中,能夠容許開發者經過Key名直接訪問對象的屬性,或者給對象的屬性賦值。而不須要調用明確的存取方法。這樣就能夠在運行時動態在訪問和修改對象的屬性。而不是在編譯時肯定,這也是iOS開發中的黑魔法之一。不少高級的iOS開發技巧都是基於KVC實現的。目前網上關於KVC的文章在很是多,有的只是簡單地說了下用法,有的講得深刻可是在使用場景和最佳實踐沒有說明,我寫下這遍文章就是給你們詳解一個最完整最詳細的KVC。
 

KVC在iOS中的定義

不管是Swift仍是Objective-C,KVC的定義都是對NSObject的擴展來實現的(Objective-c中有個顯式的NSKeyValueCoding類別名,而Swift沒有,也不須要)因此對於全部繼承了NSObject的類型,都能使用KVC(一些純Swift類和結構體是不支持KVC的),下面是KVC最爲重要的四個方法html

- (nullable id)valueForKey:(NSString *)key;                          //直接經過Key來取值
- (void)setValue:(nullable id)value forKey:(NSString *)key;          //經過Key來設值
- (nullable id)valueForKeyPath:(NSString *)keyPath;                  //經過KeyPath來取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //經過KeyPath來設值
 
 
其餘的方法
 
+ (BOOL)accessInstanceVariablesDirectly;
//默認返回YES,表示若是沒有找到Set方法的話,會按照_key,_iskey,key,iskey的順序搜索成員,設置成NO就不這樣搜索
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//KVC提供屬性值確認的API,它能夠用來檢查set的值是否正確、爲不正確的值作一個替換值或者拒絕設置新值並返回錯誤緣由。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//這是集合操做的API,裏面還有一系列這樣的API,若是屬性是一個NSMutableArray,那麼能夠用這個方法來返回

- (NSMutableOrderedSet *)mutableOrderedSetValueForKey:(NSString *)key API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));ios

- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;git

- (BOOL)validateValue:(inout id _Nullable * _Nonnull)ioValue forKeyPath:(NSString *)inKeyPath error:(out NSError **)outError;github

- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;macos

- (NSMutableOrderedSet *)mutableOrderedSetValueForKeyPath:(NSString *)keyPath API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));編程

- (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;api

- (nullable id)valueForUndefinedKey:(NSString *)key;
//若是Key不存在,且沒有KVC沒法搜索到任何和Key有關的字段或者屬性,則會調用這個方法,默認是拋出異常
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//和上一個方法同樣,只不過是設值。
- (void)setNilValueForKey:(NSString *)key;
//若是你在SetValue方法時面給Value傳nil,則會調用這個方法
- (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys;
//輸入一組key,返回該組key對應的Value,再轉成字典返回,用於將Model轉到字典。

- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;數組

 

 
 
上面的這些方法在碰到特殊狀況或者有特殊需求仍是會用到的,因此也是能夠了解一下。後面的代碼示例會有講到其中的一些方法。
同時蘋果對一些容器類好比NSArray或者NSSet等,KVC有着特殊的實現。建議有基礎的或者英文好的開發者直接去看蘋果的官方文檔,相信你會對KVC的理解更上一個臺階。(本人一直認爲英文文檔太繁瑣,一直沒有看過,有空看看)
 

KVC是怎麼尋找Key的

KVC在內部是按什麼樣的順序來尋找key的?
當調用setValue:屬性值 forKey:@」name「的代碼時,底層的執行機制以下:xcode

  • 程序優先調用set:屬性值方法,代碼經過setter方法完成設置。注意,這裏的是指成員變量名,首字母大清寫要符合KVC的全名規則,下同
  • 若是沒有找到setName:方法,KVC機制會檢查+ (BOOL)accessInstanceVariablesDirectly方法有沒有返回YES,默認該方法會返回YES,若是你重寫了該方法讓其返回NO的話,那麼在這一步KVC會執行setValue:forUNdefinedKey:方法,不過通常開發者不會這麼作。因此KVC機制會搜索該類裏面有沒有名爲_的成員變量,不管該變量是在類接口部分定義,仍是在類實現部分定義,也不管用了什麼樣的訪問修飾符,只在存在以_命名的變量,KVC均可以對該成員變量賦值。
  • 若是該類即沒有set:方法,也沒有_成員變量,KVC機制會搜索_is的成員變量,
  • 和上面同樣,若是該類即沒有set:方法,也沒有__is成員變量,KVC機制再會繼續搜索is的成員變量。再給它們賦值。
  • 若是上面列出的方法或者成員變量都不存在,系統將會執行該對象的setValue:forUNdefinedKey:方法,默認是拋出異常。

若是開發者想讓這個類禁用KVC裏,那麼重寫+ (BOOL)accessInstanceVariablesDirectly方法讓其返回NO便可,這樣的話若是KVC沒有找到set:屬性名時,會直接用setValue:forUNdefinedKey:方法。app

 

在KVC中使用KeyPath

然而在開發過程當中,一個類的成員變量有多是其餘的自定義類,你能夠先用KVC獲取出來再該屬性,而後再次用KVC來獲取這個自定義類的屬性,但這樣是比較繁瑣的,對此,KVC提供了一個解決方案,那就是鍵路徑KeyPath。

 

KVC如何處理異常

KVC中最多見的異常就是不當心使用了錯誤的Key,或者在設值中不當心傳遞了nil的值,KVC中有專門的方法來處理這些異常。
一般在用KVC操做Model時,拋出異常的那兩個方法是須要重寫的。雖然通常很小出現傳遞了錯誤的Key值這種狀況,可是若是不當心出現了,直接拋出異常讓APP崩潰顯然是不合理的。
通常在這裏直接讓這個Key打印出來便可,或者有些特殊狀況須要特殊處理。
一般狀況下,KVC不容許你要在調用setValue:屬性值 forKey:@」name「(或者keyPath)時對非對象傳遞一個nil的值。很簡單,由於值類型是不能爲nil的。若是你不當心傳了,KVC會調用setNilValueForKey:方法。這個方法默認是拋出異常,因此通常而言最好仍是重寫這個方法。

若是重寫setNilValueForKey:就沒問題了

 

KVC的內部實現機制

前面咱們對析了KVC是怎麼搜索key的。因此若是明白了key的搜索順序,是能夠本身寫代碼實現KVC的。在考慮到集合和keyPath的狀況下,KVC的實現會比較複雜,咱們只寫代碼實現最普通的取值和設值便可。

上面就是本身寫代碼實現KVC的部分功能。其中我省略了自定義KVC錯誤方法,省略了部分KVC搜索key的步驟,可是邏輯是很清晰明瞭的,後面的測試也符合預期。固然這只是我本身實現KVC的思路,Apple也許並非這麼作的。

 

KVC的使用

KVC在iOS開發中是毫不可少的利器,這種基於運行時的編程方式極大地提升了靈活性,簡化了代碼,甚至實現不少難以想像的功能,KVC也是許多iOS開發黑魔法的基礎。下面我來列舉iOS開發中KVC的使用場景

動態地取值和設值

利用KVC動態的取值和設值是最基本的用途了。相信每個iOS開發者都能熟練掌握,

用KVC來訪問和修改私有變量

對於類裏的私有屬性,Objective-C是沒法直接訪問的,可是KVC是能夠的,請參考本文前面的Dog類的例子。

Model和字典轉換

這是KVC強大做用的又一次體現,請參考我寫的iOS開發技巧系列—打造強大的BaseMod系列文章,裏面
充分地運用了KVC和Objc的runtime組合的技巧,只用了短短數行代碼就是完成了不少功能。

修改一些控件的內部屬性

這也是iOS開發中必不可少的小技巧。衆所周知不少UI控件都由不少內部UI控件組合而成的,可是Apple度沒有提供這訪問這些空間的API,這樣咱們就沒法正常地訪問和修改這些控件的樣式。而KVC在大多數狀況可下能夠解決這個問題。最經常使用的就是個性化UITextField中的placeHolderText了。
下面演示若是修改placeHolder的文字樣式。這裏的關鍵點是若是獲取你要修改的樣式的屬性名,也就是key或者keyPath名。

修改placeHolder的樣式

通常狀況下能夠運用runtime來獲取Apple不想開放的屬性名

能夠從裏面看到其餘還有不少東西能夠修改,運用KVC設值能夠得到本身想要的效果。

操做集合

Apple對KVC的valueForKey:方法做了一些特殊的實現,好比說NSArray和NSSet這樣的容器類就實現了這些方法。因此能夠用KVC很方便地操做集合

用KVC實現高階消息傳遞

當對容器類使用KVC時,valueForKey:將會被傳遞給容器中的每個對象,而不是容器自己進行操做。結果會被添加進返回的容器中,這樣,開發者能夠很方便的操做集合來返回另外一個集合。

方法capitalizedString被傳遞到NSArray中的每一項,這樣,NSArray的每一員都會執行capitalizedString並返回一個包含結果的新的NSArray。從打印結果能夠看出,全部String都成功以轉成了大寫。
一樣若是要執行多個方法也能夠用valueForKeyPath:方法。它先會對每個成員調用 capitalizedString方法,而後再調用length,由於lenth方法返回是一個數字,因此返回結果以NSNumber的形式保存在新數組裏。

用KVC中的函數操做集合

KVC同時還提供了很複雜的函數,主要有下面這些
①簡單集合運算符
簡單集合運算符共有@avg, @count , @max , @min ,@sum5種,都表示啥不用我說了吧, 目前還不支持自定義。

②對象運算符
比集合運算符稍微複雜,能以數組的方式返回指定的內容,一共有兩種:
@distinctUnionOfObjects
@unionOfObjects
它們的返回值都是NSArray,區別是前者返回的元素都是惟一的,是去重之後的結果;後者返回的元素是全集。
用法以下:

前者會將重複的價格去除後返回全部價格,後者直接返回全部的圖書價格。(由於只返回價格,沒有返回圖書,感受用處不大。)
③Array和Set操做符
這種狀況更復雜了,說的是集合中包含集合的狀況,咱們執行了以下的一段代碼:
@distinctUnionOfArrays
@unionOfArrays
@distinctUnionOfSets
@distinctUnionOfArrays:該操做會返回一個數組,這個數組包含不一樣的對象,不一樣的對象是在從關鍵路徑到操做器右邊的被指定的屬性裏
@unionOfArrays 該操做會返回一個數組,這個數組包含的對象是在從關鍵路徑到操做器右邊的被指定的屬性裏和@distinctUnionOfArrays不同,重複的對象不會被移除
@distinctUnionOfSets 和@distinctUnionOfArrays相似。由於Set自己就不支持重複。

============================================kvo=======kvo=============================================================================================================

KVO的是KeyValue Observe的縮寫,中文是鍵值觀察。這是一個典型的觀察者模式,觀察者在鍵值改變時會獲得通知。iOS中有個Notification的機制,也能夠得到通知,但這個機制須要有個Center,相比之下KVO更加簡潔而直接。

Key-Value Observing (KVO) 創建在 KVC 之上,它可以觀察一個對象的 KVC key path 值的變化。

  KVO的使用也很簡單,就是簡單的3步。

      1.註冊須要觀察的對象的屬性addObserver:forKeyPath:options:context:
      2.實現observeValueForKeyPath:ofObject:change:context:方法,這個方法當觀察的屬性變化時會自動調用
      3.取消註冊觀察removeObserver:forKeyPath:context:

 

==================================應用舉例=======================================================
 
 應用:修改textField的placeholder的字體顏色、大小
  1. textField.placeholder = @"username is in here!";  
  2. [textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];  
  3. [textField setValue:[UIFont boldSystemFontOfSize:16] forKeyPath:@"_placeholderLabel.font"];  

這裏是使用了KVC的方式

 

=====================================更多內容參考個人其餘博客===========================================================================================================================

 

參考連接

http://www.cnblogs.com/isItOk/p/5791644.html

http://www.cnblogs.com/isItOk/p/5651904.html

http://www.cnblogs.com/isItOk/p/5791638.html

http://www.cnblogs.com/isItOk/p/5651898.html

http://www.cnblogs.com/isItOk/p/6582859.html

相關文章
相關標籤/搜索