ios底层原理

iOS KVC&KVO原理浅析

2018-07-25  本文已影响0人  Jt_Self

KVC

1.简介

KVC全称是Key Value Coding(键值编码),是可以通过对象属性名称(Key)直接给属性值(value)编码(coding)“编码”可以理解为“赋值”。这样可以免去我们调用getter和setter方法,从而简化我们的代码,也可以用来修改系统控件内部属性。

所谓键值编码,并不是访问器方法的启动和实例变量的访问这种直接的方式,而是使用表示属性的字符串来间接访问对象属性值的一种结构。

KVC提供了一种间接访问其属性方法或成员变量的机制,可以通过字符串来访问对应的属性方法或成员变量。

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

2.实现原理

KVC的定义都是对NSObject的扩展来实现的,Objective-C中有个显式的NSKeyValueCoding类别名,所以对于所有继承了NSObject的类型,都能使用KVC(一些纯Swift类和结构体是不支持KVC的,因为没有继承NSObject),下面是KVC最为重要的四个方法:

- (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来设值

3.KVC补充

1. 处理不存在的key

我们可以考虑重写setValue: forUndefinedKey:方法与valueForUndefinedKey:方法

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

    NSLog(@"您设置的key:[%@]不存在", key);
    NSLog(@"您设置的value为:[%@]", value);

}

- (id)valueForUndefinedKey:(NSString *)key {

    NSLog(@"您访问的key:[%@]不存在", key);
    return nil;

}

2. 处理nil值

当程序尝试为某个属性设置nil值时,如果该属性并不接受nil值,那么程序将会自动执行该对象的setNilValueForKey:方法。我们同样可以重写这个方法:

- (void)setNilValueForKey:(NSString *)key {
    //对不能接受nil的属性进行处理
    if ([key isEqualToString:@"price"]) {
        //对应你具体的业务来处理
        price = 0;
    }else {
        [super setNilValueForKey:key];
    }
}

3. Key路径(Key Path)

KVC 同样允许我们通过关系来访问对象。假设 person 对象有属性 address,address 有属性 city,我们可以这样通过 person 来访问 city:

[person valueForKeyPath:@"address.city"];

这里我们调用 -valueForKeyPath: 而不是 -valueForKey:。


KVO

1.简介

KVO,即:Key-Value Observing,是 Objective-C 对 观察者模式(Observer Pattern)的实现。它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。

2.实现原理

苹果官方文档内容

Automatic key-value observing is implemented using a technique called isa-swizzling.

The isa pointer, as the name suggests, points to the object's class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data.

When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.

You should never rely on the isa pointer to determine class membership. Instead, you should use the class method to determine the class of an object instance.

大致意思为:

苹果使用了一种isa交换的技术,当ObjectA的被观察后,ObjectA对象的isa指针被指向了一个新建的子类NSKVONotifying_ObjectA,且这个子类重写了被观察值的setter方法和class方法,dealloc和isKVO方法,然后使ObjectA对象的isa指针指向这个新建的类,然后事实上ObjectA变为了NSKVONotifying ObjectA的实例对象,执行方法要从这个类的方法列表里找。

3.基本使用

注册观察者,实施监听

[self.person addObserver:self
              forKeyPath:@"age"
                 options:NSKeyValueObservingOptionNew
                 context:nil];

在这个回调方法里处理属性的变化

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSString *,id> *)change
                       context:(void *)context {
  //...实现监听处理
}

移除观察者

[self removeObserver:self forKeyPath:@"age"];

1.iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)

答. 当一个对象使用了KVO监听,iOS系统会修改这个对象的isa指针,改为指向一个全新的通过Runtime动态创建的子类,子类拥有自己的set方法实现,set方法实现内部会顺序调用 willChangeValueForKey 方法、原来的 setter 方法实现、didChangeValueForKey 方法,而 didChangeValueForKey 方法内部又会调用监听器的 observeValueForKeyPath:ofObject:change:context: 监听方法。

2.如何手动触发KVO

答. 被监听的属性的值被修改时,就会自动触发KVO。如果想要手动触发KVO,则需要我们自己调用 willChangeValueForKey 和 didChangeValueForKey 方法即可在不改变属性值的情况下手动触发KVO,并且这两个方法缺一不可。


参考文章:

https://juejin.im/post/5ac5f4b46fb9a028d5675645

上一篇 下一篇

猜你喜欢

热点阅读