IOS 菜鸟进阶

KVC

2018-11-28  本文已影响14人  学呀学呀总得学会了吧

KVC (Key Value Coding)

它是一种可以通过字符串的名字(key)来访问类属性的机制。通过对象属性名称(Key)直接给属性值(value)编码(coding).而不是通过调用Setter、Getter方法访问。KVC的方法定义在Foundation/NSKeyValueCoding中。

在NSKeyValueCoding中提供了KVC通用的访问方法,分别是getter方法valueForKey:和setter方法setValue:forKey:,以及其衍生的keyPath方法,这两个方法各个类通用的。并且由KVC提供默认的实现,我们也可以自己重写对应的方法来改变实现。

基础操作KVC

假设有CYXModel类与CYXShopModel类,CYXModel里面有name、product属性,CYXShopModel里面有productName属性。


keyPath

除了对当前对象的属性进行赋值外,还可以对其更“深层”的对象进行赋值。例如对当前对象的address属性的street属性进行赋值。KVC进行多级访问时,直接类似于属性调用一样用点语法进行访问即可。

通过keyPath对数组进行取值时,并且数组中存储的对象类型都相同,可以通过valueForKeyPath:方法指定取出数组中所有对象的某个字段。例如下面例子中,通过valueForKeyPath:将数组中所有对象的name属性值取出,并放入一个数组中返回。

KVC字典转模型的实现原理

假设dict字典中有name,icon的Key,CYXModel模型类中必须要有同名的name,icon属性与之相对应。

我们使用[CYXModel setValuesForKeysWithDictionary:dict];进行字典转模型。

setValuesForKeysWithDictionary:方法内部实现原理如下:

(1) 遍历字典里面所有的key和值,name,icon。

(2) 分别给属性赋值

[CYXModel setValue:dict[@"name"] forKey:@"name"];

[CYXModel setValue:dict[@"icon"] forKey:@"icon"];

setValue:forKey:方法:给模型的属性赋值

搜索规则

KVC在通过key或者keyPath进行操作的时候,可以查找属性方法、成员变量等,查找的时候可以兼容多种命名。具体的查找规则要以官方文档为主,所以我把官方文档翻译了一下写在下面。

在KVC的实现中,依赖setter和getter的方法实现,所以方法命名应该符合苹果要求的规范,否则会导致KVC失败。

在学习KVC的搜索规则前,要先弄明白一个属性的作用,这个属性在搜索过程中起到很重要的作用。这个属性表示是否允许读取实例变量的值,如果为YES则在KVC查找的过程中,从内存中读取属性实例变量的值。


基础Getter搜索模式

这是valueForKey:的默认实现,给定一个key当做输入参数,开始下面的步骤,在这个接收valueForKey:方法调用的类内部进行操作。

1、查找是否实现getter方法,依次匹配`-get<Key>` 和 `-<key>` 和 `is<Key>`,如果找到,直接返回。

    需要注意的是 :

    如果返回的是对象指针类型,则返回结果。

    如果返回的是NSNumber转换所支持的标量类型之一,则返回一个NSNumber

    否则,将返回一个NSValue

    2、当没有找到getter方法,调用accessInstanceVariablesDirectly询问

    如果返回YES, _<key>_is<Key>,<key>,is<Key>,找到了返回对应的值

    如果返回NO,结束查找。并调用 valueForUndefinedKey: 报异常

  3、如果没找到getter方法和属性值,调用 valueForUndefinedKey: 报异常

基础Setter搜索模式

这是setValue:forKey:的默认实现,给定输入参数value和key。试图在接收调用对象的内部,设置属性名为key的value,通过下面的步骤:

1.查找 set<Key>: 或 _set<Key> 命名的setter,按照这个顺序,如果找到的话,调用这个方法并将值传进去(根据需要进行对象转换)。

2.如果没有发现一个简单的setter,但是 accessInstanceVariablesDirectly 类属性返回YES,则查找一个命名规则为 _<key>  _is<Key> 、 <key> is<Key> 的实例变量。根据这个顺序,如果发现则将value赋值给实例变量。

3.如果没有发现setter或实例变量,则调用 setValue:forUndefinedKey: 方法,并默认提出一个异常,但是一个NSObject的子类可以提出合适的行为。


思考:若一个类有实例变量NSString *_foo,调用setValue: forKey: 时,可以以foo还是_foo作为key?                        foo

KVC性能

根据上面KVC的实现原理,我们可以看出KVC的性能并不如直接访问属性快,虽然这个性能消耗是微乎其微的。所以在使用KVC的时候,建议最好不要手动设置属性的setter、getter,这样会导致搜索步骤变长。

而且尽量不要用KVC进行集合操作,例如NSArray、NSSet之类的,集合操作的性能消耗更大,而且还会创建不必要的对象。

KVC作用


私有访问

根据上面的实现原理我们知道,KVC本质上是操作方法列表以及在内存中查找实例变量。我们可以利用这个特性访问类的私有变量,在.m中定义的私有成员变量和属性,都可以通过KVC的方式访问。

这个操作对readonly的属性,@protected的成员变量,都可以正常访问。如果不想让外界访问类的成员变量,则可以将accessInstanceVariablesDirectly属性赋值为NO。

@property (class,readonly)BOOL  accessInstanceVariablesDirectly;


改装默认的设置

KVC在实践中也有很多用处,例如UITabbar或UIPageControl这样的控件,系统已经为我们封装好了,但是对于一些样式的改变并没有提供足够的API,这种情况就需要我们用KVC进行操作了。

可以自定义一个UITabbar对象,然后在内部创建自己想要的视图,并通过layoutSubviews方法在内部进行重新布局。然后通过KVC的方式,将UITabbarController的tabbar属性替换为自定义的类即可。


安全性检查

KVC存在一个问题在于,因为传入的key或keyPath是一个字符串,这样很容易写错或者属性自身修改后字符串忘记修改,这样会导致Crash。

可以利用iOS的反射机制来规避这个问题,通过@selector()获取到方法的SEL,然后通过NSStringFromSelector()将SEL反射为字符串。这样在@selector()中传入方法名的过程中,编译器会有合法性检查,如果方法不存在或未实现会报黄色警告。


此文为转发用于记录总结 ,感谢大神们的总结。

了解更多:

https://blog.csdn.net/qq_18505715/article/details/80205796

https://www.jianshu.com/p/1d39bc610a5b

上一篇 下一篇

猜你喜欢

热点阅读