KVC与KVO

    xiaoxiao2021-12-15  8

     OC中具有很多的动态特性:例如动态类型,动态加载,动态绑定等,这里KVC(键值编码)与KVO(键值监听)也是比较常见的。

    1:键值编码(KVC),用于动态属性赋值操作。

    KVC的操作时由NSKeyValueCoding协议提供,NSObject对象就实现了这个协议,也就是说OC中几乎所有的对象都支持KVC操作,常用的KVC操作方法如下:

    (1):

    /**

     KVC的一般操作用于简单路径

     @param value 要设置的对象value

     @param key 要设置的对象的key

     */

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

    (2):

    /**

     KVC的常用方法二(用于复合路径)

    给某个自定义对象的某个属性赋值就是复合路径

     @param value 要设置的对象value

     @param keyPath 要设置的对象的的属性路径

     */

    -(void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

    下面通过一个例子理解KVC

    //

    //  info.h

    //  KVC

    //

    //  Created by MiHu on 2016/12/8.

    //  Copyright © 2016 MiHu. All rights reserved.

    //

    #import <Foundation/Foundation.h>

    @interface info : NSObject

    /**

     属性:成绩

     */

    @property(nonatomic,assign)NSInteger score;

    @end

    //

    //  info.m

    //  KVC

    //

    //  Created by MiHu on 2016/12/8.

    //  Copyright © 2016 MiHu. All rights reserved.

    //

    #import "info.h"

    @implementation info

    @end

    //  person.h

    //  KVC

    //

    //  Created by MiHu on 2016/12/8.

    //  Copyright © 2016 MiHu. All rights reserved.

    //

    #import <Foundation/Foundation.h>

    @class info;

    @interface person : NSObject{

        

    @private

        int _age;

    }

    /**

     属性:姓名

     */

    @property(nonatomic,strong)NSString *name;

    /**

     属性:info

     */

    @property(nonatomic,strong)info * info;

    /**

     公共方法

     */

    -(void)showMessage;

    @end

    //  person.m

    //  KVC

    //

    //  Created by MiHu on 2016/12/8.

    //  Copyright © 2016 MiHu. All rights reserved.

    //

    #import "person.h"

    @implementation person

    -(void)showMessage

    {

        NSLog(@"name=%@,age=%d",_name,_age);

    }

    @end

    //  ViewController.m

    //  KVC

    //

    //  Created by MiHu on 2016/12/8.

    //  Copyright © 2016 MiHu. All rights reserved.

    //

    #import "ViewController.h"

    #import "info.h"

    #import "person.h"

    @interface ViewController ()

    @end

    @implementation ViewController

    - (void)viewDidLoad {

        [super viewDidLoad];

        

        person * per = [[person alloc]init];

        

        [per setValue:@"jac" forKey:@"name"];

        [per setValue:@20 forKey:@"age"];//注意即使是私有变量也能访问

        

        [per showMessage];

        //输出:name=jac,age=20

        

        

        info * Info = [[info alloc]init];

        

        per.info = Info;//注意这一步一定要先给account属性赋值,否则下面按路径赋值无法成功,因为accountnil,当然这一步骤也可以写成:[per setValue:account1 forKeyPath:@"account"];

        

        [per setValue:@100 forKeyPath:@"info.score"];

        

        NSLog(@"per's balance is :%.2f",[[per valueForKeyPath:@"info.score"] floatValue]);

        //输出:per's balance is :100.00

        

    }

    @end

    KVC使用起来比较简单,但是它如何查找一个属性进行读取呢?具体查找规则(假设现在要利用KVCa进行读取):

    如果是动态设置属性,则优先考虑调用setA方法,如果没有该方法则优先考虑搜索成员变量_a,如果仍然不存在则搜索成员变量a,如果最后仍然没搜索到则会调用这个类的setValue:forUndefinedKey:方法(注意搜索过程中不管这些方法、成员变量是私有的还是公共的都能正确设置)

    如果是动态读取属性,则优先考虑调用a方法(属性agetter方法),如果没有搜索到则会优先搜索成员变量_a,如果仍然不存在则搜索成员变量a,如果最后仍然没搜索到则会调用这个类的valueforUndefinedKey:方法(注意搜索过程中不管这些方法、成员变量是私有的还是公共的都能正确读取)

    2: 键值监听KVO

    KVO其实是一种观察者模式,利用它可以很容易实现视图组件和数据模型的分离,当数据模型的属性值改变之后作为监听器 的视图组件就会被激发,激发时就会回调监听器自身。在ObjC中要实现KVO则必须实现NSKeyValueObServing协议,不过幸运的是 NSObject已经实现了该协议,因此几乎所有的ObjC对象都可以使用KVO

    在OC中使用KVO的一般方法为:

    /**

     注册指向指定key路径的监听器

     @param observer 谁来监听

     @param keyPath key路径

     @param options NSKeyValueObservingOptions

     @param context 上下文

     */

    - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

    /**

     移除指定Key路径的监听器

     @param observer 谁来监听

     @param keyPath key路径

     @param context 上下文

     */

    - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context NS_AVAILABLE(10_7, 5_0);

    /**

     回调监听:

     @param keyPath key路径

     @param object  谁发起的

     @param change  NSKeyValueChangeKey

     @param context 上下文

     */

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

    KVO的使用方法也很简单:

    通过addObserver: forKeyPath: options: context:为被监听对象(它通常是数据模型)注册监听器重写监听器的observeValueForKeyPath: ofObject: change: context:方法

    继续上面的例子介绍KVO 我们希望当对象的姓名发生改变的时会回调监听方法。那么此时姓名就作为我们的被监听对象,需要Person为它注册监听(使用addObserver: forKeyPath: options: context:);而人员Person作为监听器需要重写它的observeValueForKeyPath: ofObject: change: context:方法,当监听的成绩发生改变后会回调监听器Person监听方法(observeValueForKeyPath: ofObject: change: context:)

    #import "ViewController.h"

    #import "info.h"

    #import "person.h"

    @interface ViewController()

    @property(nonatomic,strong)person * per;

    @end

    @interface ViewController ()

    @end

    @implementation ViewController

    - (void)viewDidLoad {

        [super viewDidLoad];

        

         _per = [[person alloc]init];

        

        info *Info = [[info alloc]init];

        

        _per.info = Info;

        

        [_per setValue:@"jac" forKey:@"name"];

        [_per setValue:@90 forKeyPath:@"info.score"];

        

        [_per addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:nil];

        

        _per.name = @"jenny";

    }

    #pragma mark - 覆盖方法

    #pragma mark 重写observeValueForKeyPath方法,当姓名变化后此处获得通知

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

    {

        //只处理姓名改变

        if([keyPath isEqualToString:@"name"])

        {

            NSLog(@"我改名字了");

        }

    }

    #pragma mark 重写销毁方法

    -(void)dealloc

    {

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

    }

    转载请注明原文地址: https://ju.6miu.com/read-1000205.html

    最新回复(0)