kvc kvo 总结---180313

==================================================kvc========kvc======================================================================================================

 

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最为重要的四个方法html

- (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来设值
 
 
其余的方法
 
+ (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,那么能够用这个方法来返回

- (NSMutableOrderedSet *)mutableOrderedSetValueForKey:(NSString *)key API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));ios

- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;git

- (BOOL)validateValue:(inout id _Nullable * _Nonnull)ioValue forKeyPath:(NSString *)inKeyPath error:(out NSError **)outError;github

- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;macos

- (NSMutableOrderedSet *)mutableOrderedSetValueForKeyPath:(NSString *)keyPath API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));编程

- (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;api

- (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转到字典。

- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;数组

 

 
 
上面的这些方法在碰到特殊状况或者有特殊需求仍是会用到的,因此也是能够了解一下。后面的代码示例会有讲到其中的一些方法。
同时苹果对一些容器类好比NSArray或者NSSet等,KVC有着特殊的实现。建议有基础的或者英文好的开发者直接去看苹果的官方文档,相信你会对KVC的理解更上一个台阶。(本人一直认为英文文档太繁琐,一直没有看过,有空看看)
 

KVC是怎么寻找Key的

KVC在内部是按什么样的顺序来寻找key的?
当调用setValue:属性值 forKey:@”name“的代码时,底层的执行机制以下:xcode

  • 程序优先调用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:方法。app

 

在KVC中使用KeyPath

然而在开发过程当中,一个类的成员变量有多是其余的自定义类,你能够先用KVC获取出来再该属性,而后再次用KVC来获取这个自定义类的属性,但这样是比较繁琐的,对此,KVC提供了一个解决方案,那就是键路径KeyPath。

 

KVC如何处理异常

KVC中最多见的异常就是不当心使用了错误的Key,或者在设值中不当心传递了nil的值,KVC中有专门的方法来处理这些异常。
一般在用KVC操做Model时,抛出异常的那两个方法是须要重写的。虽然通常很小出现传递了错误的Key值这种状况,可是若是不当心出现了,直接抛出异常让APP崩溃显然是不合理的。
通常在这里直接让这个Key打印出来便可,或者有些特殊状况须要特殊处理。
一般状况下,KVC不容许你要在调用setValue:属性值 forKey:@”name“(或者keyPath)时对非对象传递一个nil的值。很简单,由于值类型是不能为nil的。若是你不当心传了,KVC会调用setNilValueForKey:方法。这个方法默认是抛出异常,因此通常而言最好仍是重写这个方法。

若是重写setNilValueForKey:就没问题了

 

KVC的内部实现机制

前面咱们对析了KVC是怎么搜索key的。因此若是明白了key的搜索顺序,是能够本身写代码实现KVC的。在考虑到集合和keyPath的状况下,KVC的实现会比较复杂,咱们只写代码实现最普通的取值和设值便可。

上面就是本身写代码实现KVC的部分功能。其中我省略了自定义KVC错误方法,省略了部分KVC搜索key的步骤,可是逻辑是很清晰明了的,后面的测试也符合预期。固然这只是我本身实现KVC的思路,Apple也许并非这么作的。

 

KVC的使用

KVC在iOS开发中是毫不可少的利器,这种基于运行时的编程方式极大地提升了灵活性,简化了代码,甚至实现不少难以想像的功能,KVC也是许多iOS开发黑魔法的基础。下面我来列举iOS开发中KVC的使用场景

动态地取值和设值

利用KVC动态的取值和设值是最基本的用途了。相信每个iOS开发者都能熟练掌握,

用KVC来访问和修改私有变量

对于类里的私有属性,Objective-C是没法直接访问的,可是KVC是能够的,请参考本文前面的Dog类的例子。

Model和字典转换

这是KVC强大做用的又一次体现,请参考我写的iOS开发技巧系列—打造强大的BaseMod系列文章,里面
充分地运用了KVC和Objc的runtime组合的技巧,只用了短短数行代码就是完成了不少功能。

修改一些控件的内部属性

这也是iOS开发中必不可少的小技巧。众所周知不少UI控件都由不少内部UI控件组合而成的,可是Apple度没有提供这访问这些空间的API,这样咱们就没法正常地访问和修改这些控件的样式。而KVC在大多数状况可下能够解决这个问题。最经常使用的就是个性化UITextField中的placeHolderText了。
下面演示若是修改placeHolder的文字样式。这里的关键点是若是获取你要修改的样式的属性名,也就是key或者keyPath名。

修改placeHolder的样式

通常状况下能够运用runtime来获取Apple不想开放的属性名

能够从里面看到其余还有不少东西能够修改,运用KVC设值能够得到本身想要的效果。

操做集合

Apple对KVC的valueForKey:方法做了一些特殊的实现,好比说NSArray和NSSet这样的容器类就实现了这些方法。因此能够用KVC很方便地操做集合

用KVC实现高阶消息传递

当对容器类使用KVC时,valueForKey:将会被传递给容器中的每个对象,而不是容器自己进行操做。结果会被添加进返回的容器中,这样,开发者能够很方便的操做集合来返回另外一个集合。

方法capitalizedString被传递到NSArray中的每一项,这样,NSArray的每一员都会执行capitalizedString并返回一个包含结果的新的NSArray。从打印结果能够看出,全部String都成功以转成了大写。
一样若是要执行多个方法也能够用valueForKeyPath:方法。它先会对每个成员调用 capitalizedString方法,而后再调用length,由于lenth方法返回是一个数字,因此返回结果以NSNumber的形式保存在新数组里。

用KVC中的函数操做集合

KVC同时还提供了很复杂的函数,主要有下面这些
①简单集合运算符
简单集合运算符共有@avg, @count , @max , @min ,@sum5种,都表示啥不用我说了吧, 目前还不支持自定义。

②对象运算符
比集合运算符稍微复杂,能以数组的方式返回指定的内容,一共有两种:
@distinctUnionOfObjects
@unionOfObjects
它们的返回值都是NSArray,区别是前者返回的元素都是惟一的,是去重之后的结果;后者返回的元素是全集。
用法以下:

前者会将重复的价格去除后返回全部价格,后者直接返回全部的图书价格。(由于只返回价格,没有返回图书,感受用处不大。)
③Array和Set操做符
这种状况更复杂了,说的是集合中包含集合的状况,咱们执行了以下的一段代码:
@distinctUnionOfArrays
@unionOfArrays
@distinctUnionOfSets
@distinctUnionOfArrays:该操做会返回一个数组,这个数组包含不一样的对象,不一样的对象是在从关键路径到操做器右边的被指定的属性里
@unionOfArrays 该操做会返回一个数组,这个数组包含的对象是在从关键路径到操做器右边的被指定的属性里和@distinctUnionOfArrays不同,重复的对象不会被移除
@distinctUnionOfSets 和@distinctUnionOfArrays相似。由于Set自己就不支持重复。

============================================kvo=======kvo=============================================================================================================

KVO的是KeyValue Observe的缩写,中文是键值观察。这是一个典型的观察者模式,观察者在键值改变时会获得通知。iOS中有个Notification的机制,也能够得到通知,但这个机制须要有个Center,相比之下KVO更加简洁而直接。

Key-Value Observing (KVO) 创建在 KVC 之上,它可以观察一个对象的 KVC key path 值的变化。

  KVO的使用也很简单,就是简单的3步。

      1.注册须要观察的对象的属性addObserver:forKeyPath:options:context:
      2.实现observeValueForKeyPath:ofObject:change:context:方法,这个方法当观察的属性变化时会自动调用
      3.取消注册观察removeObserver:forKeyPath:context:

 

==================================应用举例=======================================================
 
 应用:修改textField的placeholder的字体颜色、大小
  1. textField.placeholder = @"username is in here!";  
  2. [textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];  
  3. [textField setValue:[UIFont boldSystemFontOfSize:16] forKeyPath:@"_placeholderLabel.font"];  

这里是使用了KVC的方式

 

=====================================更多内容参考个人其余博客===========================================================================================================================

 

参考连接

http://www.cnblogs.com/isItOk/p/5791644.html

http://www.cnblogs.com/isItOk/p/5651904.html

http://www.cnblogs.com/isItOk/p/5791638.html

http://www.cnblogs.com/isItOk/p/5651898.html

http://www.cnblogs.com/isItOk/p/6582859.html

相关文章
相关标签/搜索