通过子类实现KVO,浅析KVO底层原理

通过手动实现KVO,对KVO底层原理有一定认识。

KVO只要是通过监听set方法,从而实现对该对象的监听。

要监听set方法,有两种实现方式,第一就是使用分类,重写set方法,但是这样就会覆盖父类的set方法,所以不可行,pass掉。

第二就是使用子类,把父类的isa指针改为子类。然后调用父类色set方法,最后调用回调方法,该方案可行。

首先是注册监听,在调用监听方法的时候,会动态实现子类,把observer保存到子类的属性中(弱引用weak类型,不能使用strong,会造成循环引用),并且把类型为父类的self 的 isa指针更改为子类。在调用set方法的时候,首先需要调用父类的set方法(通过把isa指针改为父类,调用父类的set方法),然后再调用监听回调方法(把父类色isa指针改回子类,取出observer,通过observer调用监听回调方法)。

废话不多说,直接上代码。

首先是结构目录,其中NSObject+LLKVO是NSObject的子类,作用是动态实现观察对象(比如Person)的子类。

NSObject+LLKVO的代码

#import <Foundation/Foundation.h>

@interface NSObject (LLKVO)
- (void)LL_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

@end

#import "NSObject+LLKVO.h"
#import <objc/message.h>

static NSString *OBSERVER = @"observer";
@implementation NSObject (LLKVO)
- (void)LL_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
    //1.创建一个类
    NSString *oldClassName = NSStringFromClass(self.class);
    NSString *newClassName = [@"LLKVO_" stringByAppendingString:oldClassName];
    Class myClass = objc_allocateClassPair(self.class, newClassName.UTF8String, 0);
    //注册类
    objc_registerClassPair(myClass);

    //2.重写setName方法

    /**
     *class 给哪个类加方法
     *sel 方法编号
     *imp 方法实现(函数指针)
     *type 返回值类型
     */
    class_addMethod(myClass, @selector(setName:), (IMP)setName, "[email protected]:@");

    //3.修改isa指针
    object_setClass(self, myClass);

    //4.将数据观察者保存到当前对象
    objc_setAssociatedObject(self, OBSERVER.UTF8String, observer, OBJC_ASSOCIATION_ASSIGN);

}

void setName(id self,SEL _cmd,NSString *newName) {

    //改为父类的类型,调用父类的set方法
    Class newClass = [self class];
    object_setClass(self, class_getSuperclass(newClass));

    void (* action1)(id,SEL,NSString *) = (void (*) (id,SEL,NSString *))objc_msgSend;

    action1(self,@selector(setName:),newName);

    //改为子类
    object_setClass(self, newClass);

    //取出观察者
    id observer = objc_getAssociatedObject(self, OBSERVER.UTF8String);
    if (observer) {

        void (* action)(id,SEL,NSString *,id,NSDictionary *,id) = (void (*) (id,SEL,NSString *,id,NSDictionary *,id)) objc_msgSend;
        action(observer,@selector(LL_observeValueForKeyPath:ofObject:change:context:),@"name",self,@{@"kind":@"1",@"new":newName},nil);

    }

}

Person的代码,很简单就定义了一个name属性,重写了下set方法

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end

#import "Person.h"

@implementation Person
- (void)setName:(NSString *)name {
    _name = name;
    NSLog(@"我重写了set方法");
}
@end

ViewController中的代码

#import "ViewController.h"
#import "NSObject+LLKVO.h"
#import "Person.h"

@interface ViewController ()
@property (nonatomic, strong) Person *person;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.person = [Person new];
    [self.person LL_addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:nil];
}

- (void)LL_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

    NSLog(@"%@",change);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    static NSInteger a = 0;
    self.person.name = [NSString stringWithFormat:@"name -- %tu",++a];
}

@end

Log如下:

2018-12-04 11:43:57.742679+0800 KVO原理浅析[1494:324838] 我重写了set方法

2018-12-04 11:43:57.743071+0800 KVO原理浅析[1494:324838] {

    kind = 1;

    new = "name -- 1";

}

通过自己实现KVO,明白了KVO的底层原理,苹果底层肯定做的更加详细,功能更加多,但是最基本的思想应该是一致的。

网上肯定有很多大神写的比我详细,底层原理剖析的更加彻底,仅以该博客记录自己对KVO的实现和理解,以后忘记了翻一下也可以快速想起。

原文地址:https://www.cnblogs.com/funny11/p/10063278.html

时间: 2024-11-12 07:11:39

通过子类实现KVO,浅析KVO底层原理的相关文章

MVC底层原理

窥探ASP.Net MVC底层原理 实现跨越Session的分布式TempData 1.问题的引出 我相信大家在项目中都使用过TempData,TempData是一个字典集合,一般用于两个请求之间临时缓存数据或者页面之间传递消息.也都知道TempData是用Session来实现的,既然是用Session来实现的,那么模式就是线程模式,这样的Session是没法用到分布式系统中的,那么在多台机器上部署,怎么做到Session在多台机器中共存,这就涉及到分布式存储.那该如何实现TempData的分布

Spring AOP底层原理

------------------siwuxie095 Spring AOP 底层原理 AOP 即 Aspect Oriented Programming,面向切面编程, 即 不通过修改源代码的方式扩展功能 「在不修改源代码的情况下,对程序进行增强」 2.AOP 采取横向抽取机制,取代了传统纵向继承体系重复性 代码 3.AOP 底层原理所使用的技术 (1)JDK 的动态代理:针对实现了接口的类产生代理 即 有接口,使用动态代理创建接口实现类的代理对象 (2)CGLIB 的动态代理:针对没有实现

Javascript属性constructor/prototype的底层原理

在Javascript语言中,constructor属性是专门为function而设计的,它存在于每一个function的prototype属性中.这个constructor保存了指向function的一个引用.在定义一个函数(代码如下所示)时, function F() { // some code } JavaScript内部会执行如下几个动作: 为该函数添加一个原形属性(即prototype对象). 为prototype对象额外添加一个constructor属性,并且该属性保存指向函数F的

一步搞清楚多态与类初始化的底层原理

首先我们先看一个段非常有代表性的代码,里面一口气牵扯到了多态和类初始化顺序知识. public class Test { public static void main(String[] args) { A test = new B(); } } class A { int value = 10; A() { System.out.println("父类构造器"); process(); } public void process() { System.out.println(&quo

Java多态的底层原理

作为一门面向对象语言,Java 拥有封装.继承.多态三大特性.多态就是允许不同类的对象对同一消息做出响应.基于多态,可以消除一些类型耦合关系,实现可替换.可扩充.Java 中使用多态特性的方法主要有,实现一个接口,实现抽象类的一个方法,覆盖父类的一个方法. Java多态的底层原理 多态的底层实现是动态绑定,即在运行时才把方法调用与方法实现关联起来.动态绑定涉及很多 JVM 的细节,全部写清楚需要很大篇幅,因此本文仅简单描述,后续会有其他文章就其中的细节一一分享. 静态绑定与动态绑定 JVM 的方

深入源码分析SpringMVC底层原理(二)

原文链接:深入源码分析SpringMVC底层原理(二) 文章目录 深入分析SpringMVC请求处理过程 1. DispatcherServlet处理请求 1.1 寻找Handler 1.2 没有找到Handler的处理 1.3 根据Handler寻找Adapter 1.4 拦截器的处理 1.5 Adapter处理请求 1.6 异常视图的处理 1.7 页面的跳转 2.总结 在上一篇文章中我们讲到了SpringMVC的初始化,分别初始化两个ApplicationContext,并且初始化一些处理器

【Servlet】(1)Servlet简介、Servlet底层原理、Servlet实现方式、Servlet生命周期

2017年07月26日 00:16:04 YI_DIAN_DIAN 阅读数:5832 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/qq_35415600/article/details/76100568 一.Servlet简介 1.Servlet定义: Servlet(Server Applet)是Java Servlet的简称,是为小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容.

Atitit.变量的定义&#160;获取&#160;储存&#160;物理结构&#160;基本类型简化&#160;隐式转换&#160;类型推导&#160;与底层原理&#160;attilaxDSL

Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL 1.1. $ 美元字符, php 黑头1 1.2. 默认变量的范围和声明:1 1.3. 变量的存储,储存在变量池里Map(varName,varVal)1 1.3.1. 复合变量1 1.4. 变量类型简化: 字符串 数字,  bool1 1.5. 变量自动隐式转换2 1.6. 类型推导2 2. 参考 复合变量2 1.1. $ 美元字符, php 黑头 1.2. 默认变量的范围和声明

Spring IoC底层原理

-------------------siwuxie095 Spring IoC 底层原理 1.IoC 即 Inversion of Control,控制反转,把对象的创建 交给 Spring 进行管理 2.IoC 容器管理 Bean 的方式: (1)基于配置文件的方式 (2)基于注解的方式 3.IoC 底层原理所使用的技术: (1)XML 配置文件 (2)dom4j 解析 XML (3)工厂模式 (5)反射 4.原始方案,耦合度太高 public class UserService{ publ