看了一遍官方文档关于kvc的介绍,总结一下,如有不对之处,请各位朋友能够指正,万分感谢。原文地址https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding。
kvc是NSKeyValueCoding非正式协议(Category)所提供的用来一种用来间接读取设置属性(基于get和set方法)的机制。
kvc也是许多Cocoa技术的基础,例如kvo,Cocoa bindings,Core Data和AppleScript-ability等等。
因为NSObject类遵守并实现了NSKeyValueCoding协议的必要方法,所以任何NSObject的子类都可以使用kvc机制。
那么kvc都可以用来做什么呢?
一.用来获取对象属性
NSKeyValueCoding协议中 定义了valueForKey和setValue:forKey:方法用来通过property名称读取。property又可分为三类:1.Attribute 代表简单的值例如标量,字符串,bool值,NSNumber和其他不可变类型例如NSColor都属于attributes。2.To-one relationships 代表有自己property的可变对象,例如银行账户对象BankAccount有可能有一个property Person对象owner,而owner可以有自己的property.(owner的property可以改变而不会影响BankAccount对ower的引用)3.To-many relationShips 代表集合对象,例如NSArray 或者 NSSet
:
@interface BankAccount : NSObject
@property( nonatomic) NSNumber* currentBalance; // An attribute
@property( nonatomic) Person* owner; // A to-one relation
@property( nonatomic) NSArray< Transaction*>* transactions; // A to-many relation@end
@interface Person :NSObject
@property(nonatomic)Address*address;// A to-one relation
@end@interface Address :NSObject
@property(nonatomic)NSString*street;// A to-one relation
@end所谓的Key即是property的名称字符串 例如我们通过kvc机制来设置currentBalance的值
[myAccount setValue:@(100.0) forKey:@"currentBalance"];
key path 就是由点语法所访问的属性 例如 owner.address.street[myAccount setValue:@"唐人街" forKeyPath:@"owner.address.street"];
使用kvc来读取属性值的三种方法:
valueForKey:如果找不到使用这个key作为属性名的属性的值就会调用valueForUndefinedKey:方法,并抛出NSUndefineKeyException异常,子类可以重写这个方法以避免系统默认的抛出异常。
valueForKeyPath:用来通过keyPath来读取属性的值。
dictionaryWithValuesForKeys: 传入key的数组返回属性和属性值所组成的字典。
同样对应于三个设置属性值的方法
- setValue:forKey:
setValue:forKeyPath:
setValuesForKeysWithDictionary: 其中字典里的每个key对应于对象属性的名称
二、用于获取可变的集合对象
尽管可以使用 setValue:forkKey和 valueForKey:来设置和读取一个集合对象,但是如果想获取可变集合要使用如下方法:
mutableArrayValueForKey:
andmutableArrayValueForKeyPath: 返回一个类似于NSMutableArray的代理对象
mutableSetValueForKey:
andmutableSetValueForKeyPath:返回一个类似于NSMutalbeSet的代理对象
mutableOrderedSetValueForKey:
andmutableOrderedSetValueForKeyPath:返回一个类似于NSMutableOrderedSet对象
三、用于集合属性的运算 包含三种基本的集合运算
1.聚合运算 NSNumber * transactionAverage = [ self . transactions valueForKeyPath: @"@avg.amount" ]; @avg求平均值
@count NSNumber * numberOfTransactions = [ self . transactions valueForKeyPath: @"@count" ];
NSDate * latestDate = [ self . transactions valueForKeyPath: @"@max.date" ];
NSDate * earliestDate = [ self . transactions valueForKeyPath: @"@min.date" ];
2.数组运算
NSArray * distinctPayees = [ self . transactions valueForKeyPath: @"@distinctUnionOfObjects.payee" ];去掉重复元素
NSArray * payees = [ self . transactions valueForKeyPath: @"@unionOfObjects.payee" ];不去掉重复元素
3.嵌套运算
NSArray*moreTransactions=@[<#transactiondata#>];
NSArray* collectedDistinctPayees=[ arrayOfArraysvalueForKeyPath: @"@distinctUnionOfArrays.payee"];
- NSArray*arrayOfArrays=@[self.transactions,moreTransactions];
NSArray * collectedPayees = [ arrayOfArrays valueForKeyPath: @"@unionOfArrays.payee" ];
四.获取非对象的属性
对于非对象的属性例如一个标量或者结构体,当你调用valueForKey:的时候 默认会自动将其转换为nsnumber或者nsvalue对象
- typedefstruct{
- floatx,y,z;
- }ThreeFloats;
- @interfaceMyClass
- @property(nonatomic)ThreeFloats threeFloats;
- @end
-
- ThreeFloats floats={1.,2.,3.};
- NSValue*value=[NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)];
- [myClass setValue:value forKey:@"threeFloats"];
- NSValue*result=[myClass valueForKey:@"threeFloats"];
五、校验属性
- Person*person=[[Personalloc]init];
- NSError*error;
- NSString*name=@"John";
- if(![person validateValue:&name forKey:@"name"error:&error]){
- NSLog(@"%@",error);
- }
- 需自己实现 validateValue:forKey方法来校验属性值是否正确。
- 六、访问器查找模式
- 基本属性的调用valueForKey:查找模式
- 1.首先查找对象实例方法如:get<Key>, <key>, is<Key> 或者_<key>如果找到了就调用然后执行步骤5,如果没找到继续执行步骤2
- 2.如果没有找到通用的读取器方法,那么就会查找实例方法:countOf<Key> 和objectIn<Key>AtIndex:(与nsarray类的原始方法类似) 和<key>AtIndexes:(对应NSArray的objectsAtIndexes:方法),如果第一个方法和另外两个方法中的一个被查找到了,那么就会创建一个能够响应所有NSArray方法的代理集合对象并返回。这个代理对象随后将所有对他调用数组对象的方法分解为countOf<Key>,ObjectIn<Key>AtIndex和<key>AtIndexs:方法的组合。实际上这个代理对象会表现的像数组一样,即使它不是一个NSArray对象。
- 3.如果既没有找到通用的存取访问方法,或者像数组一样的读取方法,那么就会查找第三类方法countOf<Key>,enumeratorOf<Key>和memberOf<Key>(对应NSSet的私有方法)。如果这三个方法都找到了,那么就会创建一个类似于NSSet的代理对象并返回。同样它会将调用的NSSet方法,分解为调用这三个方法的组合来实现。
- 4.如果以上方法都没有查找到,并且这个对象的类方法accessInstanceVariablesDirectly返回YES,那么会按顺序查找_<key>,_is<Key>,<key>,或者is<Key>
- 5.如果找到的属性值是一个对象指针,那么就返回这个结果,如果是被NSNumber支持的标量类型,那么就将他存到NSNumber实例,并返回,如果不被NSNumber支持,那么就将他装换为NSValue对象并返回。
- 6.如果上述所有方法都没有找到,那么就会调用valueForUndefinedKey:.这个方法默认抛出一个异常,但是NSObject的子类可以根据不同的key来处理。
- 基本类型的Setter方法的搜索模式
- 1.首先按顺序查找set<Key>: or _set<Key>,如果找到那么久调用它。
- 2.如果没有找到到,并且这个对象的类方法accessInstanceVariablesDirectly返回了YES,那么就会查找实例变量名称为_<key>,_is<Key>,<key>或者is<Key>,如果找到那么就直接赋值()。
- 3.如果都没找到,直接调用setValue:forUndefinedKey,这个方法会抛出异常。
- 对于可变数组的查找模式(mutalbeArrayValueForKey:)
- 1.查找一对插入删除方法insertObject:in<Key>AtIndex:和removeObjectFrom<Key>AtIndex:(对应于NSMutalbeArray的原始方法
insertObject:atIndex:
和removeObjectAtIndex:
)或者insert<Key>:atIndexes:
andremove<Key>AtIndexes: 如果找到其中任意一对插入删除方法,那么就会返回一个类似于NSMutalbeArray的代理对象,对于任何调用NSMutableArray的方法,它都会调用二者的组合来实现。
- 2.如果这个对象没有事项可变数组的方法,那么就会查找存取器方法set<Key>: (会创建新的可变数组对象,不如实现第一条中的方法效率高)
- 3.如果以上方法都没有查找到,并且accessInstanceVariablesDirectly方法返回YES,那么会查找实例变量_<key>或者<key>
- 4.如果都没有找到,返回的对象在调用NSMutableArray方法时 会调用setValue:forUndeinedKey
- 可变有序集合的查找模式(mutableOrderedSetValueForKey:)
- 1.查找
insertObject:in<Key>AtIndex:
andremoveObjectFrom<Key>AtIndex:
或者insert<Key>:atIndexes:
andremove<Key>AtIndexes:方法
- 如果找到返回的对象在调用NSMutableOrderedSet方法的时候也可能会调用
replaceObjectIn<Key>AtIndex:withObject:
orreplace<Key>AtIndexes:with<Key>:方法。
-
- 2.如果这个对象没有事项可变数组的方法,那么就会查找存取器方法set<Key>: (会创建新的可变数组对象,不如实现第一条中的方法效率高)
- 3.如果以上方法都没有查找到,并且accessInstanceVariablesDirectly方法返回YES,那么会查找实例变量_<key>或者<key>
- 4.如果都没有找到,返回的对象在调用NSMutableOrderedSet方法时 会调用setValue:forUndeinedKey
- 可变集合(mutableSetValueForKey:)
- 1.查找
add<Key>Object:
andremove<Key>Object:或者
add<Key>:
andremove<Key>:方法,如果有一个添加方法和一个删除方法被找到,那么会返回一个代理对象,对于调用NSMutableSet的方法
- 也会用到
intersect<Key>:
orset<Key>:
- 2.如果调用mutableSetValueForKey:的对象是一个managed对象,那么会停止否则继续下一步详情见 Core Data Programming Guide
- 3.如果一中的方法没有找到,并且对象不是一个托管对象,那么会查找set<Key>:
- 3.如果以上方法都没有查找到,并且accessInstanceVariablesDirectly方法返回YES,那么会查找实例变量_<key>或者<key>
- 4.如果都没有找到,返回的对象在调用NSMutableSet方法时 会调用setValue:forUndeinedKey