浅谈KVC

来源:互联网 发布:专业网络公关 编辑:程序博客网 时间:2024/06/13 04:04

    看了一遍官方文档关于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

举例:

@interfaceBankAccount : 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的值

[myAccountsetValue:@(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:froKey:
setValue:forKeyPath:
setValuesForKeysWithDictionary: 其中字典里的每个key对应于对象属性的名称
二、用于获取可变的集合对象
尽管可以使用setValue:forkKeyvalueForKey:来设置和读取一个集合对象,但是如果想获取可变集合要使用如下方法:
mutableArrayValueForKey: and mutableArrayValueForKeyPath: 返回一个类似于NSMutableArray的代理对象
mutableSetValueForKey: and mutableSetValueForKeyPath:返回一个类似于NSMutalbeSet的代理对象mutableOrderedSetValueForKey: and mutableOrderedSetValueForKeyPath:返回一个类似于NSMutableOrderedSet对象
三、用于集合属性的运算 包含三种基本的集合运算
1.聚合运算NSNumber*transactionAverage=[self.transactionsvalueForKeyPath:@"@avg.amount"]; @avg求平均值
@countNSNumber*numberOfTransactions=[self.transactionsvalueForKeyPath:@"@count"];
NSDate*latestDate=[self.transactionsvalueForKeyPath:@"@max.date"];
NSDate*earliestDate=[self.transactionsvalueForKeyPath:@"@min.date"];
2.数组运算
NSArray*distinctPayees=[self.transactionsvalueForKeyPath:@"@distinctUnionOfObjects.payee"];去掉重复元素
NSArray*payees=[self.transactionsvalueForKeyPath:@"@unionOfObjects.payee"];不去掉重复元素
3.嵌套运算
  • NSArray*moreTransactions=@[<#transactiondata#>];
  • NSArray*arrayOfArrays=@[self.transactions,moreTransactions];
  • NSArray*collectedDistinctPayees=[arrayOfArraysvalueForKeyPath:@"@distinctUnionOfArrays.payee"];
    NSArray*collectedPayees=[arrayOfArraysvalueForKeyPath:@"@unionOfArrays.payee"];
    四.获取非对象的属性
    对于非对象的属性例如一个标量或者结构体,当你调用valueForKey:的时候 默认会自动将其转换为nsnumber或者nsvalue对象
  • typedefstruct{
  • floatx,y,z;
  • }ThreeFloats;
  • @interfaceMyClass
  • @property(nonatomic)ThreeFloatsthreeFloats;
  • @end
      1. ThreeFloatsfloats={1.,2.,3.};
      2. NSValue*value=[NSValuevalueWithBytes:&floatsobjCType:@encode(ThreeFloats)];
      3. [myClasssetValue:valueforKey:@"threeFloats"];
      4. NSValue*result=[myClassvalueForKey:@"threeFloats"];
    五、校验属性
  • Person*person=[[Personalloc]init];
  • NSError*error;
  • NSString*name=@"John";
  • if(![personvalidateValue:&nameforKey:@"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: and remove<Key>AtIndexes: 如果找到其中任意一对插入删除方法,那么就会返回一个类似于NSMutalbeArray的代理对象,对于任何调用NSMutableArray的方法,它都会调用二者的组合来实现。
  • 2.如果这个对象没有事项可变数组的方法,那么就会查找存取器方法set<Key>: (会创建新的可变数组对象,不如实现第一条中的方法效率高)
  • 3.如果以上方法都没有查找到,并且accessInstanceVariablesDirectly方法返回YES,那么会查找实例变量_<key>或者<key>
  • 4.如果都没有找到,返回的对象在调用NSMutableArray方法时 会调用setValue:forUndeinedKey
  • 可变有序集合的查找模式(mutableOrderedSetValueForKey:
  • 1.查找insertObject:in<Key>AtIndex: and removeObjectFrom<Key>AtIndex: 或者insert<Key>:atIndexes: and remove<Key>AtIndexes:方法
  • 如果找到返回的对象在调用NSMutableOrderedSet方法的时候也可能会调用replaceObjectIn<Key>AtIndex:withObject: or replace<Key>AtIndexes:with<Key>:方法。
      1. 2.如果这个对象没有事项可变数组的方法,那么就会查找存取器方法set<Key>: (会创建新的可变数组对象,不如实现第一条中的方法效率高)
      2. 3.如果以上方法都没有查找到,并且accessInstanceVariablesDirectly方法返回YES,那么会查找实例变量_<key>或者<key>
      3. 4.如果都没有找到,返回的对象在调用NSMutableOrderedSet方法时 会调用setValue:forUndeinedKey
  • 可变集合(mutableSetValueForKey:
  • 1.查找add<Key>Object: and remove<Key>Object:或者add<Key>: and remove<Key>:方法,如果有一个添加方法和一个删除方法被找到,那么会返回一个代理对象,对于调用NSMutableSet的方法
  • 也会用到intersect<Key>: or set<Key>:
  • 2.如果调用mutableSetValueForKey:的对象是一个managed对象,那么会停止否则继续下一步详情见 Core Data Programming Guide
  • 3.如果一中的方法没有找到,并且对象不是一个托管对象,那么会查找set<Key>:
  • 3.如果以上方法都没有查找到,并且accessInstanceVariablesDirectly方法返回YES,那么会查找实例变量_<key>或者<key>
  • 4.如果都没有找到,返回的对象在调用NSMutableSet方法时 会调用setValue:forUndeinedKey









    原创粉丝点击