1. 基本概念
MODEL
主要是英文文檔里面經(jīng)常出現(xiàn)的一些概念,講解一下,方便英文文檔的閱讀。
iOS應(yīng)用開發(fā)是遵循MVC設(shè)計(jì)模式的,Cocoa框架用Object Modeling的規(guī)則來規(guī)范一個(gè)Model的實(shí)現(xiàn)。
ObjectModeling有如下幾個(gè)概念的規(guī)定:
Entity:表示持有數(shù)據(jù)的一個(gè)實(shí)體
Property實(shí)體中的成員,分為Attribute和:Relationship
Attribute:基本類型的成員,比如:數(shù)字、NSString。
Relationship:指向其它Entity的關(guān)系型成員,它又有to 1Relationship和to manyRelationship的區(qū)別。
AccessorMethod:getter,setter。
舉例:
如下是一個(gè)部門和員工關(guān)系的Model
部門:Department
部門名稱(NSString)
成員(NSArray)
部長(Employee)
MIC
(所有成員)
老王(一個(gè)成員)
MIB
員工:Employee
名字(NSStirng)
所屬部門(Department)
小王
MIC
使用KVC、KVO的優(yōu)勢
通過規(guī)定了一組通用的Cocoa命名法則、調(diào)用規(guī)則等,實(shí)現(xiàn)了如下功能:
2 使用一對(duì)高度規(guī)范化的訪問方法,獲取以及設(shè)置任何對(duì)象的任何屬性的值。
2 通過繼承一個(gè)特定的方法,并且指定希望監(jiān)視的對(duì)象及希望監(jiān)視的屬性名稱,就能在該對(duì)象的指定屬性的值發(fā)生改變時(shí),得到一個(gè)“通知”(盡管這不是一個(gè)真正意 義上的通知),并且得到相關(guān)屬性的值的變化(原先的值和改變后的新值)。
2 通過一個(gè)簡單的函數(shù)調(diào)用,使一個(gè)視圖對(duì)象的一個(gè)指定屬性隨時(shí)隨地都和一個(gè)控制器對(duì)象或模型對(duì)象的一個(gè)指定屬性保持同步。
2. KVC
2.1 概述
KVC是KeyValue Coding的簡稱,它是一種可以直接通過字符串的名字(key)來訪問類屬性的機(jī)制。而不是通過調(diào)用Setter、Getter方法訪問。
當(dāng)使用KVO、Core Data、CocoaBindings、AppleScript(Mac支持)時(shí),KVC是關(guān)鍵技術(shù)。
2.2 如何使用KVC
關(guān)鍵方法定義在:NSKeyValueCodingprotocol
KVC支持類對(duì)象和內(nèi)建基本數(shù)據(jù)類型。
2.2.1 獲取值
valueForKey:,傳入NSString屬性的名字。
valueForKeyPath:,傳入NSString屬性的路徑,xx.xx形式。
valueForUndefinedKey它的默認(rèn)實(shí)現(xiàn)是拋出異常,可以重寫這個(gè)函數(shù)做錯(cuò)誤處理。
2.2.2 修改值
setValue:forKey:
setValue:forKeyPath:
setValue:forUndefinedKey:
setNilValueForKey: 當(dāng)對(duì)非類對(duì)象屬性設(shè)置nil時(shí),調(diào)用,默認(rèn)拋出異常。
2.2.3 一對(duì)多關(guān)系成員的情況
mutableArrayValueForKey:有序一對(duì)多關(guān)系成員 NSArray
mutableSetValueForKey:無序一對(duì)多關(guān)系成員 NSSet
示例:
2.3 KVC的實(shí)現(xiàn)細(xì)節(jié)
搜索Setter、Getter方法
這一部分比較重要,能讓你了解到KVC調(diào)用之后,到底是怎樣獲取和設(shè)置類成員值的。
補(bǔ)充一點(diǎn)個(gè)人理解:KVC傳入的KEY為字符串,字符串轉(zhuǎn)換為OC下的類的屬性的過程基于反射機(jī)制;
2.3.1 搜索簡單的成員
如:基本類型成員,單個(gè)對(duì)象類型成員:NSInteger,NSString*成員。
a. setValue:forKey的搜索方式:
1. 首先搜索set:方法
如果成員用@property,@synthsize處理,因?yàn)锧synthsize告訴編譯器自動(dòng)生成set:格式的setter方法,所以這種情況下會(huì)直接搜索到。
注意:這里的是指成員名,而且首字母大寫。下同。
2. 上面的setter方法沒有找到,如果類方法accessInstanceVariablesDirectly返回YES(注:這是NSKeyValueCodingCatogery中實(shí)現(xiàn)的類方法,默認(rèn)實(shí)現(xiàn)為返回YES)。
那么按_,_is,,is的順序搜索成員名。
3. 如果找到設(shè)置成員的值,如果沒有調(diào)用setValue:forUndefinedKey:。
b. valueForKey:的搜索方式:
1. 首先按get、、is的順序查找getter方法,找到直接調(diào)用。如果是bool、int等內(nèi)建值類型,會(huì)做NSNumber的轉(zhuǎn)換。
2. 上面的getter沒有找到,查找countOf、objectInAtIndex:、AtIndexes格式的方法。
如果countOf和另外兩個(gè)方法中的一個(gè)找到,那么就會(huì)返回一個(gè)可以響應(yīng)NSArray所有方法的代理集合(collection proxy object)。發(fā)送給這個(gè)代理集合(collection proxy object)的NSArray消息方法,就會(huì)以countOf、objectInAtIndex:、AtIndexes這幾個(gè)方法組合的形式調(diào)用。還有一個(gè)可選的get:range:方法。
3. 還沒查到,那么查找countOf、enumeratorOf、memberOf:格式的方法。
如果這三個(gè)方法都找到,那么就返回一個(gè)可以響應(yīng)NSSet所有方法的代理集合(collection proxy object)。發(fā)送給這個(gè)代理集合(collection proxy object)的NSSet消息方法,就會(huì)以countOf、enumeratorOf、memberOf:組合的形式調(diào)用。
4. 還是沒查到,那么如果類方法accessInstanceVariablesDirectly返回YES,那么按_,_is,,is的順序直接搜索成員名。
5. 再?zèng)]查到,調(diào)用valueForUndefinedKey:。
2.3.2 查找有序集合成員,比如NSMutableArray
mutableArrayValueForKey:搜索方式如下:
1. 搜索insertObject:inAtIndex:、removeObjectFromAtIndex:或者insert:atIndexes、removeAtIndexes:格式的方法。
如果至少一個(gè)insert方法和至少一個(gè)remove方法找到,那么同樣返回一個(gè)可以響應(yīng)NSMutableArray所有方法的代理集合。那么發(fā)送給這個(gè)代理集合的NSMutableArray消息方法,以insertObject:inAtIndex:、removeObjectFromAtIndex:、insert:atIndexes、removeAtIndexes:組合的形式調(diào)用。還有兩個(gè)可選實(shí)現(xiàn)的接口:replaceObjectInAtIndex:withObject:、replaceAtIndexes:with:。
2. 否則,搜索set:格式的方法,如果找到,那么發(fā)送給代理集合的NSMutableArray最終都會(huì)調(diào)用set:方法。
也就是說,mutableArrayValueForKey取出的代理集合修改后,用set:重新賦值回去。這樣做效率會(huì)差很多,所以推薦實(shí)現(xiàn)上面的方法。
3. 否則,那么如果類方法accessInstanceVariablesDirectly返回YES,那么按_,的順序直接搜索成員名。如果找到,那么發(fā)送的NSMutableArray消息方法直接轉(zhuǎn)交給這個(gè)成員處理。
4. 再找不到,調(diào)用setValue:forUndefinedKey:。
2.3.3 搜索無序集合成員,如:NSSet。
mutableSetValueForKey:搜索方式如下:
1. 搜索addObject:、removeObject:或者add:、remove:格式的方法,如果至少一個(gè)insert方法和至少一個(gè)remove方法找到,那么返回一個(gè)可以響應(yīng)NSMutableSet所有方法的代理集合。那么發(fā)送給這個(gè)代理集合的NSMutableSet消息方法,以addObject:、removeObject:、add:、remove:組合的形式調(diào)用。還有兩個(gè)可選實(shí)現(xiàn)的接口:intersect、set:。
2. 如果reciever是ManagedObejct,那么就不會(huì)繼續(xù)搜索了。
3. 否則,搜索set:格式的方法,如果找到,那么發(fā)送給代理集合的NSMutableSet最終都會(huì)調(diào)用set:方法。也就是說,mutableSetValueForKey取出的代理集合修改后,用set:重新賦值回去。這樣做效率會(huì)差很多,所以推薦實(shí)現(xiàn)上面的方法。
4. 否則,那么如果類方法accessInstanceVariablesDirectly返回YES,那么按_,的順序直接搜索成員名。如果找到,那么發(fā)送的NSMutableSet消息方法直接轉(zhuǎn)交給這個(gè)成員處理。
5. 再找不到,調(diào)用setValue:forUndefinedKey:。
KVC還提供了下面的功能
2.4 值的正確性核查
KVC提供屬性值確認(rèn)的API,它可以用來檢查set的值是否正確、為不正確的值做一個(gè)替換值或者拒絕設(shè)置新值并返回錯(cuò)誤原因。
實(shí)現(xiàn)核查方法
為如下格式:validate:error:
如:
[cpp] view plain copy
print?
-(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;
}
-(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;
}
調(diào)用核查方法:
validateValue:forKey:error:,默認(rèn)實(shí)現(xiàn)會(huì)搜索 validate:error:格式的核查方法,找到則調(diào)用,未找到默認(rèn)返回YES。
注意其中的內(nèi)存管理問題。
2.5 集合操作
集合操作通過對(duì)valueForKeyPath:傳遞參數(shù)來使用,一定要用在集合(如:array)上,否則產(chǎn)生運(yùn)行時(shí)刻錯(cuò)誤。其格式如下:
Left keypath部分:需要操作對(duì)象路徑。
Collectionoperator部分:通過@符號(hào)確定使用的集合操作。
Rightkey path部分:需要進(jìn)行集合操作的屬性。
2.5.1 數(shù)據(jù)操作
@avg:平均值
@count:總數(shù)
@max:最大
@min:最小
@sum:總數(shù)
確保操作的屬性為數(shù)字類型,否則運(yùn)行時(shí)刻錯(cuò)誤。
2.5.2 對(duì)象操作
針對(duì)數(shù)組的情況
@distinctUnionOfObjects:返回指定屬性去重后的值的數(shù)組
@unionOfObjects:返回指定屬性的值的數(shù)組,不去重
屬性的值不能為空,否則產(chǎn)生異常。
2.5.3 數(shù)組操作
針對(duì)數(shù)組的數(shù)組情況
@distinctUnionOfArrays:返回指定屬性去重后的值的數(shù)組
@unionOfArrays:返回指定屬性的值的數(shù)組,不去重
@distinctUnionOfSets:同上,只是返回值為NSSet
示例代碼:
2.6 效率問題
相比直接訪問KVC的效率會(huì)稍低一點(diǎn),所以只有當(dāng)你非常需要它提供的可擴(kuò)展性時(shí)才使用它。