KVC是怎么访问属性的?KVO怎么实现的?

2017-07-20  本文已影响69人  test158888

KVC是怎么访问属性的

KVO是什么?

 - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
 - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

KVO是怎么实现的?

当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中被观察属性的 setter 方法,在setter方法里使其具有通知机制。因此,要想KVO生效,必须直接或间接的通过setter方法访问属性(KVC的setValue就是间接方式)。直接访问成员变量KVO是不生效的。

同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。
重新的setter方法里到底干了什么,而使其就有了通知机制呢?其实只是在setter方法里,给属性赋值的前后分别调用了两个方法

- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;
而- (void)didChangeValueForKey:(NSString *)key;会调用

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context;

这就是KVO实现的基本原理了!
当没有存取方法而通过KVC的setValue修改属性值时,同样的在运行时也会在setValue:forKey方法里默认调用上面俩方法。
其实我们也可以手动,显式的调用这两个方法,以使其具有通知机制。
下面用例子验证:

#import "ViewController.h"

@interface ViewController ()
{
    NSString            *_testStr;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 给self的添加self观察者,自己观察自己的testStr成员变量
    [self addObserver:self forKeyPath:@"testStr" options:NSKeyValueObservingOptionNew context:nil];

    [self willChangeValueForKey:@"testStr"];
    _testStr = @"this is a test"; // 直接修改成员变量的值,但是显式的、手动的调用上下俩方法,使其就有通知机制
    [self didChangeValueForKey:@"testStr"];
}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if(object == self && [keyPath isEqualToString:@"testStr"])
    {
        NSLog(@"----new:%@----",change[@"new"]);
    }else
    {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}


- (void)dealloc
{
    // 移除观察者
    [self removeObserver:self forKeyPath:@"stuName"];
}

@end
上一篇下一篇

猜你喜欢

热点阅读