KVC 和KVO

1)KVC概念
    Key-Value Coding,即键值编码。它是一种不通过存取方法,而通过属性名称字符串间接访问属性的机制。
  1.  赋值方法:
 - (void)setValue:(id)value forKey:(NSString *)key;
 2.获取属性的方法:
 - (id)valueForKey:(NSString *)key;
 3.通过path获取多层级的属性,比如:student.name

  - (id)valueForKeyPath:(NSString *)keyPath;
  - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
(在属性相当于由两部分组成:key相当于属性名,value相当于赋的值。从而达到间接赋值的作用)
2)KVO概念

     Key-Value Obersver,即键值观察。它是观察者模式的一种衍生。基本思想是,对目标对象的某属性添加观察,当该属性发生变化时,会自动的通知观察者。这里所谓的通知是触发观察者对象实现的KVO的接口方法。

** KVO是解决model和view同步的好法子。**

另外,KVO的优点是当被观察的属性值改变时是会自动发送通知的,这比通知中心需要post通知来说,简单了许多。

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实现的基本原理了!

KVO方法实现需要四步:

1.首先给目标对象的属性添加观察:

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
2. 使用set方法或点语法或setValue:forKey:改变属性值;

3.接收通知,需要注意各个参数的含义:

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context;
4.最后要移除观察者:

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

举一个例子:

#import "ViewController.h"
#import "Student.h"


@interface ViewController ()
{
    Student             *_student;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _student = [[Student alloc] init];
    _student.stuName = @"oldName_hu";
    
    // 1.给student对象的添加观察者,观察其stuName属性
    [_student addObserver:self forKeyPath:@"stuName" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    
    // 此时,stuName发生了变化
    _student.stuName = @"newName_wang";
}

// stuName发生变化后,观察者(self)立马得到通知。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    // 最好判断目标对象object和属性路径keyPath
    if(object == _student && [keyPath isEqualToString:@"stuName"])
    {
        NSLog(@"----old:%@----new:%@",change[@"old"],change[@"new"]);
    }else
    {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}


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

@end

补充:下面是自己模仿的例子打印信息

2018-03-08 21:55:41.531000+0800 Test[913:69576] 现在:

 乔布斯 

 设备信息:

 macBook 8000 

 Iphone 5000 

 Ipid 2000 

Person *psn=[[Person alloc]init];

    psn.name=@"乔布斯";

2018-03-08 21:55:41.531121+0800 Test[913:69576] KVC不同对象变化

Person *pe1=[[Person alloc]init];

    pe1.name=@"大胖";

NSLog(@"---KVC不同对象变化------%@", psn.name);

2018-03-08 21:55:41.531202+0800 Test[913:69576] ---KVC不同对象变化------乔布斯

2018-03-08 21:55:41.531286+0800 Test[913:69576] KVC相同对象变化

[psn setValue:@"牛魔王" forKey:@"name"];

    NSLog(@"---KVC相同对象变化---%@", psn.name);

2018-03-08 22:08:28.068447+0800 Test[953:82848] ---KVC相同对象变化---牛魔王

 NSLog(@"通过路径获取属性值:%@", [psn valueForKey:@"name"] );

2018-03-08 22:08:28.068531+0800 Test[953:82848] 通过路径获取属性值:牛魔王

2018-03-08 21:55:41.531616+0800 Test[913:69576] KVO机制

playTools=@"牛蛋儿";

[psn setValue:@"草蛋儿" forKey:@"playTools"];


2018-03-08 21:55:41.532100+0800 Test[913:69576] 

------New:草蛋儿

-------Old:牛蛋儿


发现:

1).在KVC的setValue间接赋值中,创建不同的对象setValue赋值修改的属性也不同;所以要求对像相统一;

2).在观察者方法中的Object(对象),keypath(属性名),change[new]  change[old]  代表最新赋值和之前的赋值;

3).KVO的优势:1. 我们可以通过实现观察者方法来获取判断想要的对象;

                        2. 属性值改变几次就会促发实现的方法几次;









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