淺談-KVC-的實現原理
KVC
是 KeyValue Coding
的簡稱,俗稱“鍵值編碼”,遵循 NSKeyValueCoding
協議,它是一種可以直接通過字符串的名字 key
來訪問類屬性的機制,而不是通過調用 setter
、 getter
方法訪問。
對於 KVC
, Cocoa
自動放入和取出基本數據類型放入 NSNumber
或 NSValue
中,當使用 setValue:ForKey:
或者 valueForKey:
時,它自動將基本數據類型從這些對象中取出,僅 KVC
具有這種自動包裝功能,常規方法調用和屬性語法不具備該功能。
setValue:forKey
的實現方式:
以字符串的形式向對象發送消息,如果成員用 @property
,因為 @synthsize
吿訴編譯器自動生成 set<Key>:
格式的 setter
方法,所以這種情況下會直接搜索到。
首先查找以 set<Key>
命名的 setter
方法,如果沒有找到查找以 _set<Key>
命名的方法;如果上面的方法沒有找到,如果類方法 accessInstanceVariablesDirectly
返回 YES
,那麼將在對象內部查找名為 _<key>
、 _is<Key>
、 <key>
、 is<key>
的實例變量。如果找到則設置成員的值,如果沒有查找調用 setValue:forUndefinedKey:
。
valueForKey:
的實現方式:
- 首先查找以
get<Key>
、<key>
、is<Key>
命名的getter
方法,找到直接調用。 - 如果上面的
getter
沒有找到,則會查找countOf<Key>
、objectIn<Key>AtIndex:
、<Key>AtIndexes
格式的方法,找到就會調用countOf<Key>
、objectIn<Key>AtIndex:
、<Key>AtIndexes
方法,還有一個可選的get<Key>:range:
方法。 - 若是還沒查到,那麼查找
countOf<Key>
、enumeratorOf<Key>
、memberOf<Key>:
格式的方法,如果找到就調用countOf<Key>
、enumeratorOf<Key>
、memberOf<Key>:
方法。 - 若是還沒查到,那麼如果類方法 accessInstanceVariablesDirectly 返回 YES,那麼將在對象內部查找名為
_<key>
、_is<Key>
、<key>
、is<key>
的實例變量。 - 再沒查到,調用
valueForUndefinedKey:
。
綜上,使用 KVC 訪問屬性的代價比直接使用存取方法性能開銷要大。
值的正確性核查
KVC 提供屬性值確認的 API,它可以用來檢查 set 的值是否正確、為不正確的值做一個替換值或者拒絕設置新值並返回錯誤原因。
實現核查方法,為如下格式: validate<Key>:error:
- (BOOL)validateName:(id *)ioValue error:(NSError **)outError { // The name must not be nil, and must be at least two characters long. if ((*ioValue == nil) || ([(NSString *)*ioValue length] < 2]) { if (outError != NULL) { NSString *errorString = NSLocalizedStringFromTable( @"A Person's name must be at least two characters long", @"Person", @"validation: too short name error"); NSDictionary *userInfoDict = [NSDictionary dictionaryWithObject:errorString forKey:NSLocalizedDescriptionKey]; *outError = [[[NSError alloc] initWithDomain:PERSON_ERROR_DOMAIN code:PERSON_INVALID_NAME_CODE userInfo:userInfoDict] autorelease]; } return NO; } return YES; }
調用核查方法:
validateValue:forKey:error:
,默認實現會搜索 validate<Key>:error:
格式的核查方法,找到則調用,未找到默認返回 YES
。