ios学习路线—Objective-C(KVC)

一.简介
全称是Key-value coding,翻译成键值编码。它提供了一种使用字符串而不是访问器方法去访问一个对象实例变量的机制。

二.KVC相关技术
1.Key和Key Paht
KVC定义了一种按名称访问对象属性的机制,支持这种访问的主要方法是:

- (id)valueForKey:(NSString *)key;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;

前边两个方法用到的Key较容易理解,就是要访问的属性名称对应的字符串。
后面两个方法用到的KeyPath是一个被点操作符隔开的用于访问对象的指定属性的字符串序列。比如KeyPath address.street将会访问消息接收对象所包含的address属性中包含的一个street属性。其实KeyPath说白了就是我们平时使用点操作访问某个对象的属性时所写的那个字符串。

2.点语法和KVC
在实现了访问器方法的类中,使用点语法和KVC访问对象其实差别不大,二者可以任意混用。但是没有访问起方法的类中,点语法无法使用,这时KVC就有优势了。

3.一对多关系(To-Many)中的集合访问器方法
当操作一对多的属性中的内容时,我们有两种选择:
(1).间接操作
先通过KVC方法取到集合属性,然后通过集合属性操作集合中的元素。
(2).直接操作
苹果为我们提供了一些方法模板,我们可以以规定的格式实现这些方法来达到访问集合属性中元素的目的。

4.键值验证(Key-Value Validation)
KVC提供了验证Key对应的Value是否可用的方法:

- (BOOL)validateValue:(inout id *)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

该方法默认的实现是调用一个如下格式的方法:

- (BOOL)validate<Key>:error:

比如属性name对应的方法为:

-(BOOL)validateName:(id *)ioValue error:(NSError * __autoreleasing *)outError {
    // Implementation specific code.
    return ...;
}

这样就给了我们一次纠错的机会。
需要指出的是, KVC是不会自动调用键值验证方法的 ,就是说我们需要手动验证。但是有些技术,比如CoreData会自动调用。

5.KVC对数值和结构体属性的支持
一套机制如果不支持数值和结构体型的数据,那么它的实用性就会大大折扣。幸运的是KVC中苹果对这方面的支持做的很好。KVC可以自动的将数值或结构体型的数据打包或解包成NSNumber或NSValue对象,以达到适配的目的。
举个例子,Person类有个个NSInteger类型的age属性
(1).修改值
我们通过KVC技术使用如下方式设置age属性的值:

[person setValue:[NSNumber numberWithInteger:5] forKey:@"age"];

我们赋给age的是一个NSNumber对象, KVC会自动的将NSNumber对象转换成NSInteger对象 ,然后再调用相应的访问器方法设置age的值。
(2).获取值
同样,以如下方式获取age属性值:

[person valueForKey:@"age"];

这时,会以NSNumber的形式返回age的值。
需要说明的是,什么时候返回的是NSNumber,什么时候返回的是NSValue?
(3).使用NSNumber封装
可以使用NSNumber的数据类型有:

+ (NSNumber *)numberWithChar:(char)value;
+ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
+ (NSNumber *)numberWithShort:(short)value;
+ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
+ (NSNumber *)numberWithInt:(int)value;
+ (NSNumber *)numberWithUnsignedInt:(unsigned int)value;
+ (NSNumber *)numberWithLong:(long)value;
+ (NSNumber *)numberWithUnsignedLong:(unsigned long)value;
+ (NSNumber *)numberWithLongLong:(long long)value;
+ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value;
+ (NSNumber *)numberWithFloat:(float)value;
+ (NSNumber *)numberWithDouble:(double)value;
+ (NSNumber *)numberWithBool:(BOOL)value;
+ (NSNumber *)numberWithInteger:(NSInteger)value NS_AVAILABLE(10_5, 2_0);
+ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value NS_AVAILABLE(10_5, 2_0);

总之就是一些常见的数值型数据。
(4).使用NSValue封装
NSValue主要用于处理结构体型的数据,它本身提供了如下集中结构的支持:

+ (NSValue *)valueWithCGPoint:(CGPoint)point;
+ (NSValue *)valueWithCGSize:(CGSize)size;
+ (NSValue *)valueWithCGRect:(CGRect)rect;
+ (NSValue *)valueWithCGAffineTransform:(CGAffineTransform)transform;
+ (NSValue *)valueWithUIEdgeInsets:(UIEdgeInsets)insets;
+ (NSValue *)valueWithUIOffset:(UIOffset)insets NS_AVAILABLE_IOS(5_0);

只有有限的6种而已!那对于其它自定义的结构体怎么办?别担心,任何结构体都是可以转化成NSValue对象的,具体实现方法参见我之前的一篇文章:
http://blog.csdn.net/wzzvictory/article/details/8614433

6.处理异常
使用kvc时,如果代码中的key值不存在,会抛出异常,可以在类中通过重写它提供下面的这个方法来解决这个问题。

- (void)setValue:(id)value forUndefinedKey:(NSString *)key;

当key不存在时,会自动调用上面的这个方法,可以在这个方法中进行处理。

7.集合运算符
集合运算符是一个特殊的Key Path,可以作为参数传递给valueForKeyPath:方法, 注意只能是这个方法 ,如果传给了valueForKey:方法保证你程序崩溃。
运算符是一个以@开头的特殊字符串
(1).简单集合运算符
简单集合运算符共有@avg,@count,@max,@min,@sum5种,都表示啥不用我说了吧, 目前还不支持自定义 。
有一个集合类的对象:transactions,它存储了一个个的Transaction类的实例,该类有三个属性:payee,amount,date。下面以此为例说明如何使用这些运算符:
要获取amount的平均值可以这样:

NSNumber *transactionAverage = [transactions valueForKeyPath:@"@avg.amount"];

要获取transactions集合中元素数目可以这样:

NSNumber *numberOfTransactions = [transactions valueForKeyPath:@"@count"];

需要之处的是,@count是这些集合运算符中比较特殊的一个,因为它没有右路经,原因很容易理解。
(2).对象运算符
比集合运算符稍微复杂,能以数组的方式返回指定的内容,一共有两种:

@distinctUnionOfObjects
@unionOfObjects

它们的返回值都是NSArray,区别是前者返回的元素都是唯一的,是去重以后的结果;后者返回的元素是全集。
用法如下:

NSArray *payees = [transactions valueForKeyPath:@"@distinctUnionOfObjects.payee"];
NSArray *payees = [transactions valueForKeyPath:@"@unionOfObjects.payee"];

前者会将收款人的姓名去除重复的以后返回,后者直接返回所有收款人的姓名。
(3).Array和Set操作符
这种情况更复杂了,说的是集合中包含集合的情况,我们执行了如下的一段代码:

// Create the array that contains additional arrays.
self.arrayOfTransactionsArray = [NSMutableArray array];

// Add the array of objects used in the above examples.
[arrayOfTransactionsArray addObject:transactions];

// Add a second array of objects; this array contains alternate values.
[arrayOfTransactionsArrays addObject:moreTransactions];

得到了一个包含集合的集合:arrayOfTransactionsArray
这时如果我们想操作 arrayOfTransactionsArray中包含的集合中的元素时,可以使用如下三个运算符:

@distinctUnionOfArrays
@unionOfArrays
@distinctUnionOfSets

前两个针对的集合是Arrays,后一个针对的集合是Sets。 因为Sets中的元素本身就是唯一的,所以没有对应的@unionOfSets运算符。
它们的用法举例如下:

NSArray *payees = [arrayOfTransactionsArrays valueForKeyPath:@"@unionOfArrays.payee"];

三.实现原理
KVC再某种程度上提供了访问器的替代方案。不过访问器方法是一个很好的东西,以至于只要是有可能,KVC也尽量再访问器方法的帮助下工作。为了设置或者返回对象属性,KVC按顺序使用如下技术:
(1).检查是否存在-、-is(只针对布尔值有效)或者-get的访问器方法,如果有可能,就是用这些方法返回值;
检查是否存在名为-set:的方法,并使用它做设置值。对于 -get和 -set:方法,将大写Key字符串的第一个字母,并与Cocoa的方法命名保持一致;
(2).如果上述方法不可用,则检查名为-_、-_is(只针对布尔值有效)、-_get和-_set:方法;
(3).如果没有找到访问器方法,可以尝试直接访问实例变量。实例变量可以是名为:或_;
(4).如果仍为找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值