zoukankan      html  css  js  c++  java
  • KVO的用法、底层实现原理

    KVO的用法

    KVO也就是key-value-observing(即键值观察),利用一个key来找到某个属性并监听其值得改变。用法如下:

    • 添加观察者
    • 在观察者中实现监听方法,observeValueForKeyPath: ofObject: change: context:(通过查阅文档可以知道,绝大多数对象都有这个方法,因为这个方法属于NSObject)
    • 移除观察者
    //让对象b监听对象a的name属性
    //options属性可以选择是哪个
     /* NSKeyValueObservingOptionNew =0x01, 新值 
      * NSKeyValueObservingOptionOld =0x02, 旧值 
      */ 
    [a addObserver:b forKeyPath:@"name"options:kNilOptionscontext:nil]; 
    a.name = @"zzz";
    #pragma mark - 实现KVO回调方法
    /* * 当对象的属性发生改变会调用该方法
        * @param keyPath 监听的属性 
        * @param object 监听的对象 
        * @param change 新值和旧值 
        * @param context 额外的数据 
    */
    - (void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)objectchange:(NSDictionary<NSString *,id>*)change context:(void *)context{ 
        NSLog(@"%@的值改变了,",keyPath); 
        NSLog(@"change:%@", change);
    }
    //最后不要忘记了,去移除observer
    - (void)dealloc{ 
           [a removeObserver:b forKeyPath:@"name"];
      }
    KVO键值观察者底层解析
    涉及到了runtime,关于isa指针
    手动实现键值观察(代码示例)

    被观察的对象Target(重写setter/getter方法)
    Target.h

    @interface Target : NSObject
    {
       int age;
    }
    // for manual KVO 
    - age- (int) age;
    - (void) setAge:(int)theAge;
    @end

    Target.m

    @implementation Target
    - (id) init{ 
        self = [super init]; 
        if (nil != self) { 
              age = 10; 
         } 
        return self;
    }
    // for manual KVO - age
    - (int) age{
        return age;
    }
    - (void) setAge:(int)theAge{ 
        [self willChangeValueForKey:@"age"];
        age = theAge; 
        [self didChangeValueForKey:@"age"];
    }
    + (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key { 
        if ([key isEqualToString:@"age"]) {
         return NO;
     } 
    return [super automaticallyNotifiesObserversForKey:key];
    }
    @end

    首先,需要手动实现属性的 setter 方法,并在设置操作的前后分别调用 willChangeValueForKey: didChangeValueForKey方法,这两个方法用于通知系统该 key 的属性值即将和已经变更了;
    其次,要实现类方法 automaticallyNotifiesObserversForKey,并在其中设置对该 key 不自动发送通知(返回 NO 即可)。这里要注意,对其它非手动实现的 key,要转交给 super 来处理。

    实现原理

    KVO的实现是基于runtime运行时的,下面就来详细介绍一下原理:还是这张图:


     
    • 当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。
    • 派生类在被重写的 setter 方法中实现真正的通知机制,就如前面手动实现键值观察那样。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制。当然前提是要通过遵循 KVO 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 KVO 的。
    • 同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。

    KVO与Notification之间的区别:

    notification是需要一个发送notification的对象,一般是notificationCenter,来通知观察者。

    KVO是直接通知到观察对象,并且逻辑非常清晰,实现步骤简单。

  • 相关阅读:
    PHP 高精度计算
    PHPWord使用方法
    羽毛球
    大数据(2)
    大数据(1)
    Centos 7 启动错误:XFS_WANT_CORRUPTED_GOTO 修复
    selenium 自动化工具
    python 开发技巧(0)-- 各个系统的python安装
    Yii简单使用阿里云短信教程!
    VMware虚拟机 Ubuntu 实用技巧 (2)桥接模式连接网络与网卡的配置
  • 原文地址:https://www.cnblogs.com/junhuawang/p/5802325.html
Copyright © 2011-2022 走看看