Objective-C:KVC
1 概述
1.1 访问方法
Key-value coding(KVC)是一种间接访问对象属性的机制,类似键值对,通过名字(或键)可以直接获得对象的属性值。事实上,key-value coding定义了一些模式和规则方法来实现属性的访问,这些基本方法在NSKeyValueCoding协议中声明,同时NSObject默认实现了该协议。
KVC存在两种形式的访问方法:
- getter:获取对象的属性值
- setter:设置对象的属性值
如下使用KVC来简化代码的编写:
1 - (id)tableView:(NSTableView *)tableview //未使用KVC的访问方法
2 objectValueForTableColumn:(id)column
3 row:(int)row
4 {
5 ChildObject *child = [childrenArray objectAtIndex:row];
6 if ( [[column identifier] isEqualToString:@"name"] )
7 {
8 return [child name];
9 }
10 if ( [[column identifier] isEqualToString:@"age"] )
11 {
12 return [child age];
13 }
14 if ( [[column identifier] isEqualToString:@"favoriteColor"] )
15 {
16 // etc...
17 }
18 // etc...
19 }
20 - (id)tableView:(NSTableView *)tableview //使用了KVC的访问方法
21 objectValueForTableColumn:(id)column
22 row:(int)row
23 {
24 ChildObject *child = [childrenArray objectAtIndex:row];
25 return [child valueForKey:[column identifier]];
26 }
1.2 访问属性
Key-value coding技术其实就是间接访问对象的属性,支持KVC的属性有三种类型:attributes、to-one relationships和to-many relationships。
1) attributes:该类型属性是普通类型,有scalar、string和Boolean 等简单类型,以及一些值对象和不可变类型,如NSNumber和NSColor。
2) to-one relationship:该类型属性是对象类型,即该类型属性本身也是一个对象(拥有自己的属性)。
3) to-many relationships:该类型属性是一种容器,主要是NSArray 和NSSet容器。
2 KVC基础
2.1 键与键路径
1) 键(keys)
键是字符串类型,用于标识对象的属性,建的命名必须遵循如下的规则:
a) 必须是 ASCII编码的。
b) 必须以小写字母打头。第一个字母可以是下划线,但不能是数字,也不能是大写字母。
c) 不能包含空格。
2) 键路径(Key Paths)
键路径也是一个键,其通过点操作符来隔开不同对象。访问一个属性的属性,就需要使用点标记指定一个更复杂的键路径。
比如:
1 [foo valueForKeyPath:@”someMember.someAttributeonMember”];
2 键路径访问 foo 对象的 someMember 特性,在 someMember 所属的类上查找someAttributeOnMember属性,并返回存储在这里的值。
2.2 Getter方法
KVC提供如下方法来获取对象的属性值:
1) valueForKey:方法
通过向该方法传递一个key(标识符),能够获得对象的属性值。若没有相应的访问方法或没有健变量,则所属对象将调用自身的valueForUndefinedKey方法,该方法默认抛出NSUndefinedKeyException异常。
2) valueForKeyPath:方法
通过向该方法传递一个key path,能够获得对象中属性的属性值。若没有键路径,同样会调用自身的valueForUndefinedKey方法。
3) dictionaryWithValuesForKeys:方法
用一个标识符数组(字符串类型)返回一个NSDictionary对象。
2.3 Setter方法
KVC提供一系列方法设置对象的属性:
1) setValue:forKey:方法
通过向该方法传递key和value,从而能够设置对象的属性值,并且该方法自动实现wrapping和unwrapping。若对象中不存在key标识符或变量,则会调用该对象的setValue:forUndefinedKey:方法,该方法的默认实现是抛出NSUndefinedKeyException类型的异常。
2) setValue:forKeyPath:方法
同样是设置对象的属性,不同的是该方法能够操纵key path。
3) setValuesForKeysWithDictionary:方法
通过向该方法传递一个NSDictionary对象,从而设置对象的属性。
2.4 访问实例
如下简单创建Myclass类,并在该类中定义了两个属性,接着通过KVC进行访问对象的属性。
1) 类定义
@interface MyClass : NSObject
@property NSString *string;
@property NSInteger integer;
@property MyClass *instance;
@end
2) 键访问
1 int main(int argc, const char * argv[])
2 {
3 MyClass *myInstance = [[MyClass alloc] init];
4
5 [myInstance setValue:@"hello world" forKey:@"string"];
6 [myInstance setValue:[NSNumber numberWithInt:100] forKey:@"integer"];
7
8 NSLog(@"%@",[myInstance valueForKey:@"string"]);
9 NSLog(@"%@",[myInstance valueForKey:@"integer"]);
10 return 0;
11 }
12 输出:
13 2016-04-06 21:04:32.533 KVC[1134:94328] hello world
14 2016-04-06 21:04:32.534 KVC[1134:94328] 100
3) 键路径访问
1 int main(int argc, const char * argv[])
2 {
3 MyClass *myInstance = [[MyClass alloc] init];
4 MyClass *anotherInstance = [[MyClass alloc] init];
5 anotherInstance.instance = myInstance;
6
7 [anotherInstance setValue: [NSNumber numberWithInt:222] forKeyPath:@"instance.integer"];
8 NSLog(@"%@",[anotherInstance valueForKeyPath:@"instance.integer"]);
9
10 return 0;
11 }
12 输出:
13 2016-04-06 21:13:46.432 KVC[1253:99388] 222
3 存取方法
虽然可以使用KVC中的访问方法valueForKey:和setValue:forKey:来操作对象的属性,但需要实现属性的存取方法来供KVC访问方法调用。
3.1 普通模式
3.1.1 取方法(getter)
对于属性的取方法有两种形式,对于不同类型的属性可以实现如下两种之一:
- -<key>:此种形式的取方法用于返回一个对象、标量或结构体等类型。
- -is<Key>:此种形式的取方法用于返回一个布尔类型的值。
如某类中的hidden属性为bool类型,可以如下两种方式实现其取方法:
1 - (BOOL)hidden
2 {
3 // Implementation specific code.
4 return ...;
5 }
6 - (BOOL)isHidden
7 {
8 // Implementation specific code.
9 return ...;
10 }
3.1.2 存方法(setter)
为了使用KVC的setValue:forKey:方法来设置属性值,需要实现对象的set<Key>:存方法。如下所示hidden属性的存方法实现。
1 - (void)setHidden:(BOOL)flag
2 {
3 // Implementation specific code.
4 return;
5 }
若设置的属性值为non-object类型(即为标量),那么还需考虑传递给存方法的参数为nil的情况。其中在Objective-C中,若给标量属性的存方法传递nil参数,那么将调用该对象的setNilValueForKey:方法。从而工程师可以重载该方法来重新设置新的值,否则将抛出异常。
如在给setHidden方法传递nil参数时,需要将其重定向为YES值,那么可以进行如下重载setNilValueForKey:方法:
1 - (void)setNilValueForKey:(NSString *)theKey
2 {
3 if ([theKey isEqualToString:@"hidden"])
4 {
5 [self setValue:[NSNumber numberWithBool:YES] forKey:@"hidden"];
6 } else
7 [super setNilValueForKey:theKey];
8 }
3.2 集合模式
若对象的属性是一个集合,也可以简单就实现-<key>和-set<Key>:两种存取方法来访问集合对象。若需要访问集合中的元素,那么需要实现更多的方法。
可以简单将Objective-C的集合类型分为两种:有序和无序。从而集合元素的存取方法也相应有两种形式:索引存取器方法和无序存取器方法。
3.2.1 有序集合
索引方法定义了集合属性的计数、查询、添加和替换等机制,其中有序集合有种两种类型:NSArray和NSMutableArray。
1) 取元素方法(不可变集合)
不可变集合只能获取集合中的元素,从而只需实现如下的方法:
- -countOf<Key>:必须实现,此方法的功能类似NSArray类的count方法。
- -objectIn<Key>AtIndex:或-<key>AtIndexes:必须实现之一,同样类似于NSArray类的objectAtIndex: and objectsAtIndexes:方法。
- -get<Key>:range:可选,其功能类似于NSArray类的getObjects:range方法。
如在某类中有个employees集合,从而可实现上述四个方法:
1 - (NSUInteger)countOfEmployees
2 {
3 return [employees count];
4 }
5 - (id)objectInEmployeesAtIndex:(NSUInteger)index
6 {
7 return [employees objectAtIndex:index];
8 } -
9 (NSArray *)employeesAtIndexes:(NSIndexSet *)indexes
10 {
11 return [employees objectsAtIndexes:indexes];
12 }
13 - (void)getEmployees:(Employee **)buffer range:(NSRange)inRange
14 {
15 // Return the objects in the specified range in the provided
16 // buffer. For example, if the employees were stored in an
17 // underlying NSArray
18 [employees getObjects:buffer range:inRange];
19 }
2) 存元素方法(可变集合)
对于可变集合(mutable)本身是可以修改集合的元素,因此除了需要实现上述的取元素方法,同时还需要实现如下的存元素方法:
a) -insertObject:in<Key>AtIndex: 或 -insert<Key>:atIndexes:
至少实现之一,其功能类似于NSMutableArray类的insertObject:atIndex和insertObjects:atIndexes:。
b) -removeObjectFrom<Key>AtIndex: 或 -remove<Key>AtIndexes:
至少实现之一,其功能类似于NSMutableArray类的removeObjectAtIndex:和removeObjectsAtIndexes:。
c)-replaceObjectIn<Key>AtIndex:withObject: 或 -replace<Key>AtIndexes:with<Key>:
可选的,若有性能上的要求可以实现其。
如在某类中有个employees集合,从而可实现上述6个方法:
1 - (void)insertObject:(Employee *)employee inEmployeesAtIndex:(NSUInteger)index
2 {
3 [employees insertObject:employee atIndex:index];
4 return;
5 }
6 -(void)insertEmployees:(NSArray *)employeeArray atIndexes:(NSIndexSet *)indexes
7 {
8 [employees insertObjects:employeeArray atIndexes:indexes];
9 return;
10 }
11 - (void)removeObjectFromEmployeesAtIndex:(NSUInteger)index
12 {
13 [employees removeObjectAtIndex:index];
14 }
15 - (void)removeEmployeesAtIndexes:(NSIndexSet *)indexes
16 {
17 [employees removeObjectsAtIndexes:indexes];
18 }
19 - (void)replaceObjectInEmployeesAtIndex:(NSUInteger)index withObject:(id)anObject
20 {
21 [employees replaceObjectAtIndex:index withObject:anObject];
22 }
23 -(void)replaceEmployeesAtIndexes:(NSIndexSet *)indexes withEmployees:(NSArray *)employeeArray
24 {
25 [employees replaceObjectsAtIndexes:indexes withObjects:employeeArray];
26 }
3.2.2 无序集合
1) 取元素方法(不可变集合)
对于无序的集合来说,一般的取元素方法是获取迭代器和判断是否为成员对象。为了满足只读需要,必须实现如下所有的方法。
- -countOf<Key>:类似NSSet类的count方法;
- -enumeratorOf<Key>:类似NSSet类的objectEnumerator方法;
- -memberOf<Key>:类似NSSet类的member方法;
如下是实现例子:
1 - (NSUInteger)countOfTransactions
2 {
3 return [transactions count];
4 }
5 -(NSEnumerator *)enumeratorOfTransactions
6 {
7 return [transactions objectEnumerator];
8 }
9 -(Transaction *)memberOfTransactions:(Transaction *)anObject
10 {
11 return [transactions member:anObject];
12 }
2) 存元素方法(可变集合)
为了容易且高效的利用mutableSetValueForKey:方法来访问无序集合的元素,需要实现如下方法:
a) -add<Key>Object: 或 -add<Key>:
至少实现两者之一,功能类似于NSMutableSet类的addObject:方法。
b) -remove<Key>Object: 或 -remove<Key>:
至少实现两者之一,功能类似于NSMutableSet类的removeObject:方法。
c) -intersect<Key>:
可选类型,功能类似于NSSet 类的intersectSet: 方法。
如下的测试例子:
1 - (void)addTransactionsObject:(Transaction *)anObject
2 {
3 [transactions addObject:anObject];
4 }
5 -(void)addTransactions:(NSSet *)manyObjects
6 {
7 [transactions unionSet:manyObjects];
8 }
9 - (void)removeTransactionsObject:(Transaction *)anObject
10 {
11 [transactions removeObject:anObject];
12 }
13 -(void)removeTransactions:(NSSet *)manyObjects
14 {
15 [transactions minusSet:manyObjects];
16 }
17 - (void)intersectTransactions:(NSSet *)otherObjects
18 {
19 return [transactions intersectSet:otherObjects];
20 }
4 搜索模式
4.1 简单属性
4.1.1 默认setValue:forKey
当为了设置对象的某个属性,而调用其setValue:forKey:方法时,将按如下顺序执行搜素:
1) 首先会搜索接收对象的set<Key>:方法,即setValue:forKey:方法所属的对象。
2) 若未找到匹配的方法,且接收对象的accessInstanceVariablesDirectly方法会返回YES,那么将在接收对象中按序搜素以这样命名的实例变量:_<key>、_is<Key>、<key>、和is<Key>。
3) 若找到匹配的方法,那么会调用匹配的方法;若需要可以设置Non-Object值。
4) 若没有匹配方法或实例变量被发现,那么会调用接收对象的setValue:forUndefinedKey:方法。
4.1.2 默认valueForKey
当为了访问对象的某个属性,而调用其valueForKey:方法时,将按如下的顺序执行搜索:
a) 首先会在接收对象中按序搜索get<Key>、<key>、和is<Key>方法,若找到其中之一的方法,并且该方法返回的是Objective-C类型的指针,则直接返回;若是找到的方法是返回支持NSNumber的标量类型,那么将执行转换。
b) 若未找到匹配的方法,则在接收对象中按序搜素countOf<Key>、objectIn<Key>AtIndex:、<key>AtIndexes:方法。
c) 若仍未找到匹配的方法,则在接收对象中按序搜索countOf<Key>、enumeratorOf<Key>、和memberOf<Key>:方法。
d) 若未找到匹配的方法,且接收对象的accessInstanceVariablesDirectly方法会返回YES,那么将在接收对象中按序搜素以这样命名的实例变量:_<key>、_is<Key>、<key>、和is<Key>。
e) 若上述所有情况都未发现,那么将调用接收对象的valueForUndefinedKey:方法。
Objective-C:KVC的更多相关文章
- 转:KVC/KVO原理详解及编程指南
作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/9674431 转载请注明出处 如果觉得文章对你有所帮助,请通过留言或 ...
- ios开发runtime学习五:KVC以及KVO,利用runtime实现字典转模型
一:KVC和KVO的学习 #import "StatusItem.h" /* 1:总结:KVC赋值:1:setValuesForKeysWithDictionary实现原理:遍历字 ...
- 转:KVC 与 KVO 理解
KVC 与 KVO 理解 On 2012 年 6 月 7 日, in iPhone, by donly KVC 与 KVO 是 Objective C 的关键概念,个人认为必须理解的东西,下面是实例讲 ...
- UI:KVO、KVC
什么是KVC 什么是 KVO ? KVC:(NSKey ValueCoding)”键-值 编码“是一种间接的访问对象属性(字符串表征)的机制.对象的属性都可以通过使用KVC机制用相同的方式访问.我们 ...
- iOS - 详细理解KVC与KVO
详细理解KVC与KVO 在面试的时候,KVC与KVO有些时候还是会问到的,并且他们都是Objective C的关键概念,在这里我们先做一个简单地介绍: (一)KVC: KVC即指:NSKeyValue ...
- KVC技巧二则
说两个与KVC相关的技巧. 1.KVC与字典 有时候我们需要取出嵌套字典中的某个键的值.例如某个嵌套字典: NSDictionary *dict = @{@"subDict":@{ ...
- KVC&&&KVO
KVC 什么是KVC --->What KVC指的就是NSKeyValueCoding非正式协议. KVC是一种间接地访问对象的属性的机制. 这种间接表现在通过字符串来标识属性,而不是通过调用存 ...
- 07OC之KVC、KVO
在OC中,有着很多动态的特性,今天我们着重讲讲OC中的键值编码(KVC)和键值监听(KVO)特性. 一.键值编码(KVC) 在C#中,我们可以通过反射的方式动态去读写一个对象,有时候很方便,因为可以利 ...
- Objective-C的 KVC和KVO
字面意思分别是: KVC是指key value coding,键值编码. KVO是指key value observing,键值观察. 直白的说法是: KVC就是将一个对象的属性及其值当做一个字典,可 ...
随机推荐
- Java 内存区域和GC机制--备用
Java垃圾回收概况 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代 ...
- asp.net学习
http://www.cnblogs.com/fish-li/archive/2011/12/27/2304063.html
- 【Tools】maven安装
安装Maven插件老是报以下的错误,好像少了一个叫guava库的东西,但是在其他机器安装不报这个错误. Cannot complete the install because one or more ...
- 纯CSS实现delay连续动画
从前css3还没出来的时候,用jquery的delay方法可以串起一个一个独立的动画片段. 那么在不使用jquery的平台上,如何借助css3来完成一些列动作呢? 有高人做了一个动感十足的人物动画: ...
- Linux Kernel 'perf_event.c'本地权限提升漏洞
漏洞版本: Linux Kernel 3.11-rc4 漏洞描述: Linux Kernel是一款开源的操作系统 Linux Kernel 'perf_event.c'存在一个安全漏洞,允许本地攻击者 ...
- Node.js权威指南 (10) - Node.js中的错误处理与断言处理
10.1 使用domain模块处理错误 / 272 10.1.1 domain模块概述 / 272 10.1.2 创建并使用Domain对象 / 274 10.1.3 隐式绑定与显式绑定 / 276 ...
- Google Map API 学习六
今天其实收货很大的 1.new google.maps.Circle 就是如何在地图上标注一个圆 3.getAnimation 在这里是通过获取Marker是否存在动作,然后如果存在动作的话,就将动作 ...
- TFS 2012使用简介(一)
最近,一直想让团队加入敏捷开发,但TFS2010用下来,并不是令人满意,还好,TFS2012横空出世了.相比TFS2010,TFS2012改进太多了,主要体现在以下方面: Team Web Acces ...
- windows下protobuf jar包的编译
0.如果你不想手动编译生成,请直接跳到最后下载附件. 1.下载protobuf release版本:https://github.com/google/protobuf/releases,protoc ...
- Django教程:第一个Django应用程序(4)
Django教程:第一个Django应用程序(4) 2013-10-09 磁针石 #承接软件自动化实施与培训等gtalk:ouyangchongwu#gmail.comqq 37391319 #博客: ...