KVC(Key-value coding)键值编码,单看这个名字可能不太好理解。其实翻译一下就很简单了,就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性。而不是在编译时确定,这也是iOS开发中的黑魔法之一。很多高级的iOS开发技巧都是基于KVC实现的。目前网上关于KVC的文章在非常多,有的只是简单地说了下用法,有的讲得深入但是在使用场景和最佳实践没有说明,我写下这遍文章就是给大家详解一个最完整最详细的KVC。
KVC在iOS中的定义
无论是Swift还是Objective-C,KVC的定义都是对NSObject的扩展来实现的(Objective-c中有个显式的NSKeyValueCoding
类别名,而Swift没有,也不需要)所以对于所有继承了NSObject在类型,都能使用KVC(一些纯Swift类和结构体是不支持KVC的),下面是KVC最为重要的四个方法
1 2 3 4 |
- (nullable id)valueForKey:(NSString *)key; //直接通过Key来取值 - (void)setValue:(nullable id)value forKey:(NSString *)key; //通过Key来设值 - (nullable id)valueForKeyPath:(NSString *)keyPath; //通过KeyPath来取值 - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通过KeyPath来设值 |
当然NSKeyValueCoding类别中还有其他的一些方法,下面列举一些
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
+ (BOOL)accessInstanceVariablesDirectly; //默认返回YES,表示如果没有找到Set方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索 - (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError; //KVC提供属性值确认的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因。 - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key; //这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回 - (nullable id)valueForUndefinedKey:(NSString *)key; //如果Key不存在,且没有KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常 - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key; //和上一个方法一样,只不过是设值。 - (void)setNilValueForKey:(NSString *)key; //如果你在SetValue方法时面给Value传nil,则会调用这个方法 - (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys; //输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。 |
上面的这些方法在碰到特殊情况或者有特殊需求还是会用到的,所以也是可以了解一下。后面的代码示例会有讲到其中的一些方法。
同时苹果对一些容器类比如NSArray或者NSSet等,KVC有着特殊的实现。建议有基础的或者英文好的开发者直接去看苹果的官方文档,相信你会对KVC的理解更上一个台阶。
KVC是怎么寻找Key的
KVC是怎么使用的,我相信绝大多数的开发者都很清楚,我在这里就不再写简单的使用KVC来设值和取值的代码了,首页我们来探讨KVC在内部是按什么样的顺序来寻找key的。
当调用setValue:属性值 forKey:@”name“
的代码时,底层的执行机制如下:
- 程序优先调用
set:属性值
方法,代码通过setter方法完成设置。注意,这里的是指成员变量名,首字母大清写要符合KVC的全名规则,下同 - 如果没有找到setName:方法,KVC机制会检查
+ (BOOL)accessInstanceVariablesDirectly
方法有没有返回YES,默认该方法会返回YES,如果你重写了该方法让其返回NO的话,那么在这一步KVC会执行setValue:forUNdefinedKey:
方法,不过一般开发者不会这么做。所以KVC机制会搜索该类里面有没有名为_
的成员变量,无论该变量是在类接口部分定义,还是在类实现部分定义,也无论用了什么样的访问修饰符,只在存在以_
命名的变量,KVC都可以对该成员变量赋值。 - 如果该类即没有
set:
方法,也没有_
成员变量,KVC机制会搜索_is
的成员变量, - 和上面一样,如果该类即没有
set:
方法,也没有_
和_is
成员变量,KVC机制再会继续搜索和
is
的成员变量。再给它们赋值。 - 如果上面列出的方法或者成员变量都不存在,系统将会执行该对象的
setValue:forUNdefinedKey:
方法,默认是抛出异常。
如果开发者想让这个类禁用KVC里,那么重写+ (BOOL)accessInstanceVariablesDirectly
方法让其返回NO即可,这样的话如果KVC没有找到set:
属性名时,会直接用setValue:forUNdefinedKey:
方法。
下面我们来让代码来测试一下上面的KVC机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
@interface Dog : NSObject @end @implementation Dog { NSString* toSetName; NSString* isName; //NSString* name; NSString* _name; NSString* _isName; } // -(void)setName:(NSString*)name{ // toSetName = name; // } //-(NSString*)getName{ // return toSetName; //} +(BOOL)accessInstanceVariablesDirectly{ return NO; } -(id)valueForUndefinedKey:(NSString *)key{ NSLog(@"出现异常,该key不存在%@",key); return nil; } -(void)setValue:(id)value forUndefinedKey:(NSString *)key{ NSLog(@"出现异常,该key不存在%@",key); } @end int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... Dog* dog = [Dog new]; [dog setValue:@"newName" forKey:@"name"]; NSString* name = [dog valueForKey:@"toSetName"]; NSLog(@"%@",name); rayon-line crayon-striped-line" id="crayon-5812b0ada3950029766043-34"> NSLog(@"%@",name); 调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性。而不是在编译时确定,这也是iOS开发中的黑魔法之一。很多高级的iOS开发技巧都是基于KVC实现的。目前网上关于KVC的文章在非常多,有的只是简单地说了下用法,有的讲得深入但是在使用场景和最佳实践没有说明,我写下这遍文章就是给大家详解一个最完整最详细的KVC。
KVC在iOS中的定义无论是Swift还是Objective-C,KVC的定义都是对NSObject的扩展来实现的(Objective-c中有个显式的
当然NSKeyValueCoding类别中还有其他的一些方法,下面列举一些
上面的这些方法在碰到特殊情况或者有特殊需求还是会用到的,所以也是可以了解一下。后面的代码示例会有讲到其中的一些方法。 KVC是怎么寻找Key的KVC是怎么使用的,我相信绝大多数的开发者都很清楚,我在这里就不再写简单的使用KVC来设值和取值的代码了,首页我们来探讨KVC在内部是按什么样的顺序来寻找key的。
如果开发者想让这个类禁用KVC里,那么重写 下面我们来让代码来测试一下上面的KVC机制
|