美文网首页
iOS 通过RunTime重写KVO

iOS 通过RunTime重写KVO

作者: IT_Bear_ | 来源:发表于2019-03-08 16:33 被阅读0次

KVO原理:当一个对象被观察时, 系统会新建一个子类NSNotifying_A ,在子类中重写了对象被观察属性的 set方法, 并且改变了该对象的 isa 指针的指向(指向了新建的子类) , 当属性的值发生改变了, 会调用子类的set方法, 然后发出通知

一. 创建NSObject分类, 创建类方法和回调用的block

typedef void(^IBKVOBlock)(NSDictionary *_Nonnull dictionary);
@interface NSObject (IBKVO)
+ (void)IB_AddObserverWithKeyPath:(NSString *)keypath option:(NSKeyValueObservingOptions)option block:(IBKVOBlock)block;
@end

二. 具体代码以及注解

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

static const char *IBKVO_getter = "IBKVO_getter";
static const char *IBKVO_setter = "IBKVO_setter";
static const char *IBKVO_block = "IBKVO_block";

@implementation NSObject (IBKVO)

+ (void)IB_AddObserverWithKeyPath:(NSString *)keypath option:(NSKeyValueObservingOptions)option block:(IBKVOBlock)block
{
    //创建子类 默认子类格式:'IBKVO'+'Notifying_'+ClassName
    NSString *oldClassName = NSStringFromClass([self class]);
    NSString *newClassName = [NSString stringWithFormat:@"IBKVONotifying_%@",oldClassName];
    
    //判断子类是否存在 不存在就创建一个并注册
    Class subClass = objc_getClass(newClassName.UTF8String);
    if (!subClass) {
        subClass = objc_allocateClassPair([self class], newClassName.UTF8String, 0);
        objc_registerClassPair(subClass);
    }
    
    //将keypath 首字母大写 拼成setValue方法名
    NSString *KeyPath = [[keypath substringToIndex:1] uppercaseString];
    NSString *setKeyPath = [@"set" stringByAppendingString:KeyPath];
    
    //判断属性存不存在
    Method setM = class_getInstanceMethod(subClass, NSSelectorFromString(setKeyPath));
    if (!setM) {
        @throw [NSExpression expressionWithFormat:@"属性不存在"];
    }
    
    SEL setSel = NSSelectorFromString([setKeyPath stringByAppendingString:@":"]);
    
    //添加set方法------
    //先找到本来的set方法的type
    Method setMethod = class_getInstanceMethod([self class], NSSelectorFromString(setKeyPath));
    const char* setTypes = method_getTypeEncoding(setMethod);
    
    //添加自定义的set方法 监听set方法的调用 从而block回调
    class_addMethod(subClass, setSel, (IMP)setMethod, setTypes);
    
    //将self指向子类
    object_setClass(self, subClass);
    
    //保存set get方法名
    objc_setAssociatedObject(self, IBKVO_setter, setKeyPath, OBJC_ASSOCIATION_COPY_NONATOMIC);
    objc_setAssociatedObject(self, IBKVO_getter, keypath, OBJC_ASSOCIATION_COPY_NONATOMIC);
    //保存block
    objc_setAssociatedObject(self, IBKVO_block, block, OBJC_ASSOCIATION_COPY);
}

void setMethod(id self, SEL _cmd,id newValue)
{
    //获取getter setter 名
    NSString *setterName = objc_getAssociatedObject(self, IBKVO_setter);
    NSString *getterName = objc_getAssociatedObject(self, IBKVO_getter);
    
    //保存subclass
    Class subClass = [self class];
    
    //self(isa) 指向super
    object_setClass(self, class_getSuperclass(subClass));
    
    //获取更改前的值 必须让self指向super才能获取
    id oldValue = objc_msgSend(self, NSSelectorFromString(getterName));
    //调用setter 赋给新的值
    objc_msgSend(self, NSSelectorFromString([setterName stringByAppendingString:@":"]),newValue);
    
    //记录前后改变的值
    NSMutableDictionary *change = [NSMutableDictionary new];
    if (newValue) {
        change[NSKeyValueChangeNewKey] = newValue;
    }
    if (oldValue) {
        change[NSKeyValueChangeOldKey] = oldValue;
    }
    
    //返回给调用者 类似于delegate
    IBKVOBlock block = objc_getAssociatedObject(self, IBKVO_block);
    if (block) {
        block(change);
    }
    // isa 指向子类
    object_setClass(self, subClass);
}
@end

相关文章

  • iOS 通过RunTime重写KVO

    KVO原理:当一个对象被观察时, 系统会新建一个子类NSNotifying_A ,在子类中重写了对象被观察属性的 ...

  • iOS Runtime学习笔记 (二) - 实战应用

    iOS runtime实战应用 iOS runtime 进行添加属性,并支持KVO监听 iOS 中category...

  • iOS 自定义KVO

    自己实现kvo之前,需要知道iOS系统对kvo的实现。 系统实现kvo的原理 这依赖了OC强大的runtime特性...

  • KVO 的本质?

    iOS 用什么方式实现对一个对象的 KVO ? (KVO 的本质是什么?) 首先利用 Runtime API 动态...

  • KVO应用、原理及自实现

    一.KVO简介 KVO 是ios里,观察者设计模式的一种应用实现,依赖runtime,基于KVC,KVO提供了一种...

  • ios kvo 的实现原理,自定义kvo

    kvo的使用: 原理:首先要知道 kvo 是通过 runtime 实现的 在使用kvo观察某对象的时候(例:对象 ...

  • iOS性能优化 - 启动优化

    KVO的本质: 本质是重写set方法。 1、利用Runtime API 动态生成一个全新的子类,并且让instan...

  • KVO的本质

    KVO的本质: 本质是重写set方法。 1、利用Runtime API 动态生成一个全新的子类,并且让instan...

  • 这些个技术要求

    iOS下的网络通信机制, 内存管理机制, GCD, Block, KVC, KVO, Runtime, Runlo...

  • iOS_KVO本质解析

    iOS 用什么方式实现对一个对象的KVO?(KVO的本质是什么) 利用Runtime API动态生成一个子类, 并...

网友评论

      本文标题:iOS 通过RunTime重写KVO

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