詳解KVC(轉載)

原文地址:http://ios.jobbole.com/84954/

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最為重要的四個方法

  • (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來設值

當然NSKeyValueCoding類別中還有其他的一些方法,下面列舉一些

  • (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,那么可以用這個方法來返回
  • (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轉到字典。
  • (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,那么可以用這個方法來返回
  • (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轉到字典。
    上面的這些方法在碰到特殊情況或者有特殊需求還是會用到的,所以也是可以了解一下。后面的代碼示例會有講到其中的一些方法。
    同時蘋果對一些容器類比如NSArray或者NSSet等,KVC有著特殊的實現。建議有基礎的或者英文好的開發者直接去看蘋果的官方文檔,相信你會對KVC的理解更上一個臺階。

KVC是怎么尋找Key的

KVC是怎么使用的,我相信絕大多數的開發者都很清楚,我在這里就不再寫簡單的使用KVC來設值和取值的代碼了,首頁我們來探討KVC在內部是按什么樣的順序來尋找key的。
當調用setValue:屬性值 forKey:@”name“的代碼時,底層的執行機制如下:

程序優先調用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:方法。

下面我們來讓代碼來測試一下上面的KVC機制

@interface Dog : NSObject
@end
@implementation Dog
{
NSString* toSetName;
NSString* isName;
//NSString* name;
NSString* _name;
NSString* _isName;
}
// -(void)setName:(NSString)name{
// toSetName = name;
// }
//-(NSString
)getName{
// return toSetName;
//}
+(BOOL)accessInstanceVariablesDirectly{
return NO;
}
-(id)valueForUndefinedKey:(NSString )key{
NSLog(@"出現異常,該key不存在%@",key);
return nil;
}
-(void)setValue:(id)value forUndefinedKey:(NSString )key{
NSLog(@"出現異常,該key不存在%@",key);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Dog
dog = [Dog new];
[dog setValue:@"newName" forKey:@"name"];
NSString
name = [dog valueForKey:@"toSetName"];
NSLog(@"%@",name);
}
return 0;
}

  • (nullable id)valueForKey:(NSString *)key; //直接通過Key來取值
  • (void)setValue:(nullab
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • KVC(Key-value coding)鍵值編碼,單看這個名字可能不太好理解。其實翻譯一下就很簡單了,就是指iO...
    朽木自雕也閱讀 1,580評論 6 1
  • KVC簡單介紹 KVC(Key-value coding)鍵值編碼,就是指iOS的開發中,可以允許開發者通過Key...
    公子無禮閱讀 1,425評論 0 6
  • KVC(Key-value coding)鍵值編碼,單看這個名字可能不太好理解。其實翻譯一下就很簡單了,就是指iO...
    黑暗中的孤影閱讀 49,974評論 74 441
  • KVC(Key-value coding)鍵值編碼,單看這個名字可能不太好理解。其實翻譯一下就很簡單了,就是指iO...
    Fendouzhe閱讀 686評論 0 6
  • 簡介 KVC(Key-value coding)鍵值編碼,翻譯一下就是指iOS的開發中,可以允許開發者通過Key名...
    6ffd6634d577閱讀 1,335評論 1 9