KVC訪問器實現詳細

分類:技術 時間:2016-10-25

前言

本文翻譯自蘋果文檔 Accessor Search Implementation Details 及方法的注釋。翻譯的不對的地方還請多多包涵指正,謝謝~

翻譯背景

在做熱修復的過程中,看到 JSPatch 的OC setter方法轉義成JavaScript代碼時,感到奇妙。代碼如下:

@interface WMPatchTest
@property (nonatomic, strong) NSString *name;
@end

@implementation WMPatchTest
- (void)setName:(NSString *)name {
    _name = name;
}
@end
defineClass('WMPatchTest', {
    setPayCompletion: function(name) {
        self.setValue_forKey(name, quot;_namequot;);
    },
});

代碼中在使用setKey:value:函數時,用的是 _name ,直接對protected屬性 _name 賦值。在用@property作屬性聲明,且getter和setter方法沒有都手動同時實現情況下,系統會自動創建一個protected屬性,屬性名是在property前面加上下劃線_ property。

Then,若想再深入具體了解KVC,請看下面翻譯的蘋果文檔~

KVC訪問器實現詳細

在KVC直接訪問實例變量前,會嘗試使用屬性的訪問方法。本篇文章講述了KVC是如何決定用哪種方法訪問屬性。

對于簡單屬性的 -setValue:forKey:

-setValue:forKey: 方法對一個屬性的默認實現調用后,判斷執行的順序如下:

  1. 首先搜索調用類的實例方法,實例方法的名字模式是 setlt;Keygt; (即set字符串和key值的組合)。如果方法找到了,系統還會校驗方法參數。如果參數類型不是對象指針(id),但參數值為nil,則 -setNilValueForKey: 被調用。 -setNilValueForKey: 默認實現會拋出一個 NSInvalidArgumentException 異常,但你可以重寫這個方法的實現。如果參數類型是對象指針,方法會簡單地校驗通過。若參數類型是其他類型(比如,int,CGPoint等),在方法 -valueForKey: 調用前會自動把這些類型轉換成NSNumber或NSValue;
  2. 如果方法沒找到,而且調用該方法的類方法 accessInstanceVariablesDirectly 返回的是YES,那么會查找該類實例的變量名字,依次匹配這些樣式: _lt;keygt;, _islt;Keygt;, lt;keygt;, islt;Keygt; 。如果找到匹配的變量,且變量是對象型,那么對象引用計數會增1且變量也會被賦值,之后原來變量指向的舊值引用計數會減1。若實例變量是其他類型(比如,int,CGPoint等),會想步驟1中一樣,先把NSNumber或NSValue轉成非對象類型再賦值;
  3. 否則(方法和實例變量都沒找到),會調用 -setValue:forUndefinedKey: 方法。該方法默認實現會拋出一個 NSUndefinedKeyException 異常,但你可以重寫它;

兼容性注意:

  • 對于 -takeValue:forKey: 方法的向后二進制兼容,在步驟1中若方法名模式是 -_setlt;Keygt;: 也會被識別。不過在Mac10.3系統后,KVC中以下劃線開頭的模式方法已廢棄;
  • 對于向后二進制兼容性,如果調用類參數不是對象型,則在步驟1中 -unableToSetNilForKey: 方法會代替 -setNilValueForKey: 調用;
  • 步驟2中描述的行為與 -takeValue:forKey: 不同,后者在搜索類實例變量時只會去依次匹配 lt;keygt;, _lt;keygt; 模式;
  • 步驟3中對于 -takeValue:forKey: 方法,如果調用類參數不是對象型, -handleTakeValue:forUnboundKey: 會代替 -setValue:forUndefinedKey: 的調用;

栗子分析

再回頭看背景介紹的例子,name是WMPatchTest類屬性,編譯器會自動生成setter方法 -setName: ,getter方法 -name ,及私有變量 _name 。執行[WMPatchTest setValue:@quot;xxquot; forKey:@quot;_namequot;]時,首先進行步驟1,查找名字為 set_name 方法。發現沒有后,進行步驟2,檢查發現 accessInstanceVariablesDirectly 為YES后(默認是YES),依次讓類變量匹配這些樣式: _name, _isName, name, isName_name 被匹配并執行賦值操作;

后語

開發中,KVC的設置方法使用很頻繁,深入理解內部細節有助于我們開發提供更多思路,遇到異常情況時思路更清晰~


Tags: iOS開發

文章來源:http://www.jianshu.com/p/880957c3280f


ads
ads

相關文章
ads

相關文章

ad