KVO 和 KVC

一、KVO

KVO的是KeyValue Observe的缩写,中文是键值观察。这是一个典型的观察者模式,利用它可以很容易使用实现视图组件和数据模型的分离,当数据模型的属性值改变之后作为监听器的视图组件就会被激发,激发时就会回调监听器自身,在Objc中实现KVO则必须实现NSKeyValueObServing协议,不过幸运的是NSObject已经实现了该协议,因此几乎所哟的NSObjectd对象都可以使用KVO。iOS中有个Notification的机制,也可以获得通知,但这个机制需要有个Center,相比之下KVO更加简洁而直接。

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

      1.注册需要观察的对象的属性addObserver:forKeyPath:options:context:

      2.实现observeValueForKeyPath:ofObject:change:context:方法,这个方法当观察的属性变化时会自动调用

      3.取消注册观察removeObserver:forKeyPath:context:

这里有两种方式,一种是匹配keyPath,另一种是使用context

Demo:事列

A.注册需要观察的对象

- (void)viewDidLoad

{

    [super viewDidLoad];


    // 注册监听

    [self.moveView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];


    self.p.name = @"小明";

    [self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:PersonAgeContext];

}

B.实现

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

{

    // 方式1.匹配keypath

    if ([keyPath isEqualToString:@"frame"]) {

        NSLog(@"self.moveView.y = %f", self.moveView.y);

    }


    // 方式2.上下文

    if (context == PersonAgeContext) {

        NSLog(@"%@%d岁了", self.p.name, self.p.age);

    }

}

C.移除监听

-(void)dealloc // ARC模式下

{

    [self.moveView removeObserver:self forKeyPath:@"frame"];


    [self.p removeObserver:self forKeyPath:@"age" context:PersonAgeContext];

}

KVO的 优势 :

        1.能够提供一种简单的方法实现两个对象间的同步。例如:model和view之间同步;

        2.能够对非我们创建的对象,即内部对象的状态改变作出响应,而且不需要改变内部对象(SKD对象)的实现;

        3.能够提供观察的属性的最新值以及先前值;

        4.用key paths来观察属性,因此也可以观察嵌套对象;

        5.完成了对观察对象的抽象,因为不需要额外的代码来允许观察值能够被观察

      缺点 :

        1.我们观察的属性必须使用strings来定义。因此在编译器不会出现警告以及检查;

        2.对属性重构将导致我们的观察代码不再可用;

        3.复杂的“IF”语句要求对象正在观察多个值。这是因为所有的观察代码通过一个方法来指向;

        4.当释放观察者时不需要移除观察者。


二、KVC

KVC的常用方法:

- (id)valueForKey:(NSString *)key; -(void)setValue:(id)value forKey:(NSString *)key;

valueForKey的方法根据key的值读取对象的属性,setValue:forKey:是根据key的值来写对象的属性。

注意:

(1). key的值必须正确,如果拼写错误,会出现异常

(2). 当key的值是没有定义的,valueForUndefinedKey:这个方法会被调用,如果你自己写了这个方法,key的值出错就会调用到这里来

(3). 因为类key反复嵌套,所以有个keyPath的概念,keyPath就是用.号来把一个一个key链接起来,这样就可以根据这个路径访问下去

(4). NSArray/NSSet等都支持KVC


KVC过程讲解:

以 [object setValue:@"134567" forKey:@"uid"];为例子,来探究KVC的实现过程

第一步:搜索

1、首先搜索setKey:方法.(key指成员变量名, 首字母大写)

2、上面的setter方法没找到, 如果类方法accessInstanceVariablesDirectly返回YES. 那么按 _key, _isKey,key, iskey的顺序搜索成员名.(NSKeyValueCodingCatogery中实现的类方法, 默认实现为返回YES)

3、如果没有找到成员变量, 调用setValue:forUnderfinedKey:

第二步:编译器处理

被编译器处理后:

// 首先找到对应sel

SEL sel = sel_get_uid("setValue:forKey:");

// 根据object->isa找到sel对应的IMP实现指针

IMP method = objc_msg_lookup (object->isa,sel);

// 调用指针完成KVC赋值

method(object, sel, @"134567", @"uid");

解释:

1.先根据方法名通过C函数sel_get_uid拿到选择子sel

2.使用C函数objc_msg_lookup通过对象指针,选择子获取函数实现指针

3. 调用C函数method(object, sel, @"134567", @"uid"),实现KVC赋值


引申:setValue和setObject区别

setObject:ForKey: 是NSMutableDictionary特有的;setValue:ForKey:是KVC的主要方法

setobject中的key和value可以为除了nil外的任何对象

setValue中的key只能为字符串 value可以为nil也可以为空对象[NSNull null]以及全部对象

你可能感兴趣的:(KVO 和 KVC)