這篇KVC的機制寫的比較清楚,轉載地址:http://blog.csdn.net/omegayy/article/details/7381301
1. 基本概念
MODEL
主要是英文文檔裏面經常出現的一些概念,講解一下,方便英文文檔的閱讀。
IOS應用開發是遵循MVC設計模式的,Cocoa框架用Object Modeling的規則來規範一個Model的實現。
ObjectModeling有如下幾個概念的規定:
Entity:表示持有數據的一個實體
Property實體中的成員,分爲Attribute和:Relationship
Attribute:基本類型的成員,比如:數字、NSString。
Relationship:指向其它Entity的關係型成員,它又有to 1Relationship和to manyRelationship的區別。
AccessorMethod:getter,setter。
舉例:
如下是一個部門和員工關係的Model
部門:Department
部門名稱(NSString) |
成員(NSArray) |
部長(Employee) |
MIC |
(所有成員) |
老王(一個成員) |
MIB |
|
|
員工:Employee
名字(NSStirng) |
所屬部門(Department) |
小王 |
MIC |
|
|
使用KVC、KVO的優勢
通過規定了一組通用的Cocoa命名法則、調用規則等,實現瞭如下功能:
² 使用一對高度規範化的訪問方法,獲取以及設置任何對象的任何屬性的值。
² 通過繼承一個特定的方法,並且指定希望監視的對象及希望監視的屬性名稱,就能在該對象的指定屬性的值發生改變時,得到一個“通知”(儘管這不是一個真正意 義上的通知),並且得到相關屬性的值的變化(原先的值和改變後的新值)。
² 通過一個簡單的函數調用,使一個視圖對象的一個指定屬性隨時隨地都和一個控制器對象或模型對象的一個指定屬性保持同步。
2. KVC
2.1 概述
KVC是KeyValue Coding的簡稱,它是一種可以直接通過字符串的名字(key)來訪問類屬性的機制。而不是通過調用Setter、Getter方法訪問。
當使用KVO、Core Data、CocoaBindings、AppleScript(Mac支持)時,KVC是關鍵技術。
2.2 如何使用KVC
關鍵方法定義在:NSKeyValueCodingprotocol
KVC支持類對象和內建基本數據類型。
2.2.1 獲取值
valueForKey:,傳入NSString屬性的名字。
valueForKeyPath:,傳入NSString屬性的路徑,xx.xx形式。
valueForUndefinedKey它的默認實現是拋出異常,可以重寫這個函數做錯誤處理。
2.2.2 修改值
setValue:forKey:
setValue:forKeyPath:
setValue:forUndefinedKey:
setNilValueForKey: 當對非類對象屬性設置nil時,調用,默認拋出異常。
2.2.3 一對多關係成員的情況
mutableArrayValueForKey:有序一對多關係成員 NSArray
mutableSetValueForKey:無序一對多關係成員 NSSet
示例:
2.3 KVC的實現細節
搜索Setter、Getter方法
這一部分比較重要,能讓你瞭解到KVC調用之後,到底是怎樣獲取和設置類成員值的。
2.3.1 搜索簡單的成員
如:基本類型成員,單個對象類型成員:NSInteger,NSString*成員。
a. setValue:forKey的搜索方式:
1. 首先搜索set<Key>:方法
如果成員用@property,@synthsize處理,因爲@synthsize告訴編譯器自動生成set<Key>:格式的setter方法,所以這種情況下會直接搜索到。
注意:這裏的<Key>是指成員名,而且首字母大寫。下同。
2. 上面的setter方法沒有找到,如果類方法accessInstanceVariablesDirectly返回YES(注:這是NSKeyValueCodingCatogery中實現的類方法,默認實現爲返回YES)。
那麼按_<key>,_is<Key>,<key>,is<key>的順序搜索成員名。
3. 如果找到設置成員的值,如果沒有調用setValue:forUndefinedKey:。
b. valueForKey:的搜索方式:
1. 首先按get<Key>、<key>、is<Key>的順序查找getter方法,找到直接調用。如果是bool、int等內建值類型,會做NSNumber的轉換。
2. 上面的getter沒有找到,查找countOf<Key>、objectIn<Key>AtIndex:、<Key>AtIndexes格式的方法。
如果countOf<Key>和另外兩個方法中的一個找到,那麼就會返回一個可以響應NSArray所有方法的代理集合(collection proxy object)。發送給這個代理集合(collection proxy object)的NSArray消息方法,就會以countOf<Key>、objectIn<Key>AtIndex:、<Key>AtIndexes這幾個方法組合的形式調用。還有一個可選的get<Key>:range:方法。
3. 還沒查到,那麼查找countOf<Key>、enumeratorOf<Key>、memberOf<Key>:格式的方法。
如果這三個方法都找到,那麼就返回一個可以響應NSSet所有方法的代理集合(collection proxy object)。發送給這個代理集合(collection proxy object)的NSSet消息方法,就會以countOf<Key>、enumeratorOf<Key>、memberOf<Key>:組合的形式調用。
4. 還是沒查到,那麼如果類方法accessInstanceVariablesDirectly返回YES,那麼按_<key>,_is<Key>,<key>,is<key>的順序直接搜索成員名。
5. 再沒查到,調用valueForUndefinedKey:。
2.3.2 查找有序集合成員,比如NSMutableArray
mutableArrayValueForKey:搜索方式如下:
1. 搜索insertObject:in<Key>AtIndex:、removeObjectFrom<Key>AtIndex:或者insert<Key>:atIndexes、remove<Key>AtIndexes:格式的方法。
如果至少一個insert方法和至少一個remove方法找到,那麼同樣返回一個可以響應NSMutableArray所有方法的代理集合。那麼發送給這個代理集合的NSMutableArray消息方法,以insertObject:in<Key>AtIndex:、removeObjectFrom<Key>AtIndex:、insert<Key>:atIndexes、remove<Key>AtIndexes:組合的形式調用。還有兩個可選實現的接口:replaceObjectIn<Key>AtIndex:withObject:、replace<Key>AtIndexes:with<Key>:。
2. 否則,搜索set<Key>:格式的方法,如果找到,那麼發送給代理集合的NSMutableArray最終都會調用set<Key>:方法。
也就是說,mutableArrayValueForKey取出的代理集合修改後,用set<Key>:重新賦值回去。這樣做效率會差很多,所以推薦實現上面的方法。
3. 否則,那麼如果類方法accessInstanceVariablesDirectly返回YES,那麼按_<key>,<key>的順序直接搜索成員名。如果找到,那麼發送的NSMutableArray消息方法直接轉交給這個成員處理。
4. 再找不到,調用setValue:forUndefinedKey:。
2.3.3 搜索無序集合成員,如:NSSet。
mutableSetValueForKey:搜索方式如下:
1. 搜索add<Key>Object:、remove<Key>Object:或者add<Key>:、remove<Key>:格式的方法,如果至少一個insert方法和至少一個remove方法找到,那麼返回一個可以響應NSMutableSet所有方法的代理集合。那麼發送給這個代理集合的NSMutableSet消息方法,以add<Key>Object:、remove<Key>Object:、add<Key>:、remove<Key>:組合的形式調用。還有兩個可選實現的接口:intersect<Key>、set<Key>:。
2. 如果reciever是ManagedObejct,那麼就不會繼續搜索了。
3. 否則,搜索set<Key>:格式的方法,如果找到,那麼發送給代理集合的NSMutableSet最終都會調用set<Key>:方法。也就是說,mutableSetValueForKey取出的代理集合修改後,用set<Key>:重新賦值回去。這樣做效率會差很多,所以推薦實現上面的方法。
4. 否則,那麼如果類方法accessInstanceVariablesDirectly返回YES,那麼按_<key>,<key>的順序直接搜索成員名。如果找到,那麼發送的NSMutableSet消息方法直接轉交給這個成員處理。
5. 再找不到,調用setValue:forUndefinedKey:。
KVC還提供了下面的功能
2.4 值的正確性覈查
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。
注意其中的內存管理問題。
2.5 集合操作
集合操作通過對valueForKeyPath:傳遞參數來使用,一定要用在集合(如:array)上,否則產生運行時刻錯誤。其格式如下:
Left keypath部分:需要操作對象路徑。
Collectionoperator部分:通過@符號確定使用的集合操作。
Rightkey path部分:需要進行集合操作的屬性。
2.5.1 數據操作
@avg:平均值
@count:總數
@max:最大
@min:最小
@sum:總數
確保操作的屬性爲數字類型,否則運行時刻錯誤。
2.5.2 對象操作
針對數組的情況
@distinctUnionOfObjects:返回指定屬性去重後的值的數組
@unionOfObjects:返回指定屬性的值的數組,不去重
屬性的值不能爲空,否則產生異常。
2.5.3 數組操作
針對數組的數組情況
@distinctUnionOfArrays:返回指定屬性去重後的值的數組
@unionOfArrays:返回指定屬性的值的數組,不去重
@distinctUnionOfSets:同上,只是返回值爲NSSet
示例代碼:
2.6 效率問題
相比直接訪問KVC的效率會稍低一點,所以只有當你非常需要它提供的可擴展性時才使用它。