美文网首页
KVO&KVC分析

KVO&KVC分析

作者: woniu | 来源:发表于2018-04-03 16:12 被阅读11次

    KVO和KVC应用还是比较广泛的。所以,今天我们就重新再学习下它们,以加深记忆。

    一、KVO

    1、KVO(key-value observing)定义

    顾名思义“键值观察”,当观察对象发生改变的时候,对象会得到通知,然后做出相应的处理。观察者观察的是属性,只有遵循 KVO 变更属性值的方式才会执行 KVO 的回调方法,例如是否执行了 setter 方法、或者是否使用了 KVC 赋值。
    如果赋值没有通过 setter 方法或者 KVC,而是直接修改属性对应的成员变量,例如:仅调用 _name = @"newName",这时是不会触发 KVO 机制,更加不会调用回调方法的。
    所以使用 KVO 机制的前提是遵循 KVO 的属性设置方式来变更属性值。

    2、KVO的基本原理
    • KVO是基于runtime机制实现的

    • 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制

    • 如果原类为Person,那么生成的派生类名为NSKVONotifying_Person

    • 每个类对象中都有一个isa指针指向当前类,当一个类对象第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法

    • 键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey:一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。

    3、使用流程

    a、注册观察者,实施监听;
    b、在回调方法中处理属性发生的变化;
    c、移除观察者;

    4、方法分析

    a、注册观察者

    //第一个参数 observer:观察者 (这里观察self.myKVO对象的属性变化)
    //第二个参数 keyPath: 被观察的属性名称(这里观察 self.myKVO 中 num 属性值的改变)
    //第三个参数 options: 观察属性的新值、旧值等的一些配置(枚举值,可以根据需要设置,例如这里可以使用两项)
    //第四个参数 context: 上下文,可以为 KVO 的回调方法传值(例如设定为一个放置数据的字典)
    [self.myKVO addObserver:self forKeyPath:@"num" options:
    NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
    

    b、属性变化收到的通知

    //keyPath:属性名称
    //object:被观察的对象
    //change:变化前后的值都存储在 change 字典中
    //context:注册观察者时,context 传过来的值
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
    {
    }
    

    二、KVC

    KVC(Key-value coding)顾名思义键值编码。简单来说,是可以通过对象属性名称(Key)直接给属性值(value)编码(coding),也可以说赋值。这样可以免去调用setter、getter方法,从而简化我们的代码。最最重要的是,它还可以用来修改系统控件内部的属性,我们在自定义控件属性的时候经常会用到(需要配合runtime)。

    #import <Foundation/Foundation.h>
    #import "KVO.h"
    @interface KVC : NSObject
    @property (nonatomic,copy) NSString *name;
    @property (nonatomic,retain) KVO *product;
    @end
    
    #import <Foundation/Foundation.h>
    
    @interface KVO : NSObject
    @property (nonatomic,assign) int num;
    @property (nonatomic,copy) NSString *productName;
    @end
    
    
    使用场景:
     1、runtime中修改属性参数的地方,如导航栏透明、pageControl的背景图片。
     2、普通的对属性赋值。不过我们一般就直接访问属性了,这么做反而会麻烦些,且效率不高。
    

    注意:KVC的性能并不能直接访问属性快,虽然这个性能消耗是微乎其微的。所以在使用KVC的时候,建议最好不要手动设置属性的setter、getter,这样会导致搜索步骤变长。
    而且尽量不要用KVC进行集合操作,例如NSArray、NSSet之类的,集合操作的性能消耗更大,而且还会创建不必要的对象。

     
        KVC *model = [[KVC alloc]init];
    
        self.myKVO = [[KVO alloc]init];//大哥,别忘了初始化啊,这么低级的错误。
        /*
         1、未使用KVC的赋值
         myKVC.name = @"zhangsan";
         self.myKVO.productName = @"productName";
         myKVC.KVOModel = self.myKVO;
         self.myKVO.productName = @"productName";
         
         NSLog(@"---------------kvo:%@",self.myKVO.productName);
         */
        //2、使用KVC的赋值
        [model setValue:@"wangwu" forKey:@"name"];
     
        model.product = self.myKVO;
        NSLog(@"----------KVC:%@",[model valueForKeyPath:@"name"]);
    
        /*
         直接提取KVOModel中的属性值,用“.”分割
         因为类key反复嵌套,所以有个keyPath的概念,keyPath就是用.号来把一个一个key链接起来,这样就可以根据这个路径访问下去.
         */
        [model setValue:@"zhangsanfeng" forKeyPath:@"product.productName"];
    
        NSLog(@"----------kvc:%@",[model valueForKeyPath: @"product.productName"]);
    

    runtime配合KVC修改属性

    功能:修改UINavigationBar的左右两边的视图透明度。
    注意:我们获取的_liftViews,_rightViews发现在iOS11里面不能用了,使用的话会崩溃。我们这里列出来它的调用方法作为示例分析。
    [[self valueForKey:@"_liftViews"] enumerateObjectsUsingBlock:^(UIView *view, NSUInteger i, BOOL *stop) {
            view.alpha = alpha;
        }];
    
        [[self valueForKey:@"_rightViews"] enumerateObjectsUsingBlock:^(UIView *view, NSUInteger i, BOOL *stop) {
            view.alpha = alpha;
        }];
    
        UIView *titleView = [self valueForKey:@"_titleView"];
        titleView.alpha = alpha;
    

    三、扩展

    1、KVC与KVO的不同?

    KVC(键值编码),即使用字符串访问一个实例变量的机制。而不是通过调用setter/getter等方法显式的存储方式访问。

    2、notification比KVO有什么区别?

    a、notification比KVO多了发送通知的一步,但是对象之间的直接交互,通知要明显的多,需要notificationCenter来做为中间交互。而KVO如我们介绍的,设置观察者->处理属性变化,至于中间通知这一环,是由系统处理的。
    b、notification的优点时监听不局限于属性的变化,还可以对多种多样的变化进行监听,范围更广泛。(键盘、前后台等)

    3、和delegate的不同?

    相同点:
    a、delegate,KVO和NSNotification的作用都是类与类之间的通信。
    不同点:
    a、KVO和NSNotification都是负责发送接收通知,剩下的事情交给系统处理,所以不用返回值,而delegate 则需要通信的对象通过变量(代理)联系
    b、delegate是一对一,KVO和NSNotification这两个可以一对多。

    Demo地址:https://github.com/caiqingchong/KVO-KVC.git
    参考链接:
    https://www.jianshu.com/p/e59bb8f59302
    https://www.jianshu.com/p/7ba3d0eb4908

    相关文章

      网友评论

          本文标题:KVO&KVC分析

          本文链接:https://www.haomeiwen.com/subject/amqchftx.html