zoukankan      html  css  js  c++  java
  • KVO的使用三:基于runtime实现KVO

             苹果的KVO原理通过isa-swizzling技术实现,本质实现逻辑是在runtime时添加一个子类,重写set方法进行操作,现在我们也基于runtime来实现一个KVO。

           首先新建一个Person类,继承自NSObject,添加一个name属性。

           然后给NSObject添加一个分类KVO,在分类中实现KVO的注册方法EZ_addObserver:forKeyPath: options:context:,这个方法的作用和系统的注册方法一样。先为Person动态添加一个子类EZKVO_Person,然后为其添加一个setName方法,因为我们要监听的是那么的变化。

    - (void)EZ_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
        // 动态添加一个类
        NSString *className = [@"EZKVO_" stringByAppendingString:NSStringFromClass([self class])];
        const char *utfName = [className UTF8String];
        Class myClass = objc_allocateClassPair([self class], utfName, 0);
        
        // 添加setter方法
        class_addMethod(myClass, @selector(setName:), (IMP)setName, "v@:i");
        
        // 注册新添加的这个类
        objc_registerClassPair(myClass);
        
        // 修改被观察者的isa指针,isa指针指向Person类改为指向myclass类
        object_setClass(self, myClass);
        
        // 将观察者的属性保存到当前类里面去
        objc_setAssociatedObject(self, (__bridge const void *)@"objc", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }

           上面的是注册方法,下面处理的是在更改name属性值的时候,通过重写的set方法,将消息发出给观察者让其收到键值变化。过程是先取出被观察者,然后发送消息(即observeValueForKeyPath:ofObject:change:context:)让观察者接收。当然,也可以逻辑严谨些像系统那样向父类逐层调用,这里就不写出来了。

     

    void setName(id self, SEL _cmd, id name) {
        // 取出观察者
        id objc = objc_getAssociatedObject(self, (__bridge const void *)@"objc");
     
        // 通知观察者
        ((void(*)(id,SEL,id,id,id,id))objc_msgSend)(objc, @selector(observeValueForKeyPath:ofObject:change:context:), name, self, nil, nil);
    }

     

           最后沿用前面两篇文章的方法,在VC里添加触摸事件改变name的值,并在消息接收方法里打印信息(方便测试,就用了keyPath来代替change保存键值)。

     

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        self.person.name = @"小明";
    }

     

          消息接收

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        NSLog(@"name属性的改变为%@",keyPath);
    }

         结果:2018-07-22 23:29:49.085057+0800 KVODemo[4402:212515] name属性的改变为小明

     

     

  • 相关阅读:
    事务
    数据库三大范式
    jdbc
    Jdbc事务
    Android 学习笔记2开发软件的常识&搭建开发环境(windows7)
    2011年起航
    Android 学习笔记3Android 工程目录介绍&程序执行过程
    我的第一篇日志基于AT89S52的单片机的LED点阵显示屏的设计
    算法基础之快速排序
    LeetCode88.合并两个有序数组
  • 原文地址:https://www.cnblogs.com/xuanyishare/p/9352169.html
Copyright © 2011-2022 走看看