美文网首页
KVO的本质-另附runtime代码实现

KVO的本质-另附runtime代码实现

作者: zolobdz | 来源:发表于2020-01-10 14:10 被阅读0次

先上简单的背景代码:

@interface ViewController ()
@property(nonatomic, strong) Dog *dog;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.dog = [Dog new];
    self.dog.say = @"wow";
    NSLog(@"class name before kvo:%s",object_getClassName(self.dog));
    [self.dog addObserver:self forKeyPath:@"say" options:NSKeyValueObservingOptionNew context:nil];
    NSLog(@"class name after kvo:%s",object_getClassName(self.dog));
    self.dog.say = @"yeah";
}

- (void)dealloc{
    [self.dog removeObserver:self forKeyPath:@"setSay"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"%@",change);
}

Log

god:wow
class name before kvo:Dog
class name after kvo:NSKVONotifying_Dog
god:yeah
{
    kind = 1;
    new = yeah;
}

可以注意到kvo之后对象dog的类型改变了。
可以猜想设置了kvo之后:
1.runtime重新生成了一个类型
2.然后将原对象的指针指向他
3.在新类型中动态添加新的方法来转接原来的setXX方法
3.1转发方法给父类属性set
3.2通知监听对象属性发生了改变

#import <Foundation/Foundation.h>
#import "Dog.h"
#import <objc/runtime.h>
#import <objc/message.h>
@implementation Dog

// 给子类提供的IMP
void kvoMethond(id obj,SEL sel, NSString * str) {
    // 创建父类实例-superDog
    struct objc_super superDog = {
        obj,
        class_getSuperclass([obj class])
    };
    // super.setName
    objc_msgSendSuper(&superDog, sel, str);
    
    NSString *selString = [NSStringFromSelector(sel) substringToIndex:NSStringFromSelector(sel).length-1];
    NSString *key = [NSStringFromSelector(sel) substringWithRange:NSMakeRange(3, selString.length - 3)];
    key = [[key substringToIndex:1].lowercaseString stringByAppendingString:[key substringFromIndex:1]];
    // 通知观察者调用监听方法
    // 获取observer
    id observer = objc_getAssociatedObject(obj, "dog_observer");
    [observer observeValueForKeyPath:key ofObject:obj change:@{@"new": str} context:nil];
}

- (void)setSay:(NSString *)say {
    _say = say;
    NSLog(@"god:%@",say);
}

// 自定义实现的kvo
- (void)dog_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context {
    
    const char* kvoClassName = [[NSString stringWithFormat:@"KVONotification_%@",NSStringFromClass(self.class)] UTF8String];
    // 动态创建子类
    Class kvoClass = objc_allocateClassPair(self.class, kvoClassName, 0);
    // 将self的isa指向新的子类
    object_setClass(self, kvoClass);
    
    NSString *pathName = [NSString stringWithFormat:@"set%@:",keyPath.capitalizedString];
    SEL sel = NSSelectorFromString(pathName);
    // 给sel(setName)设置新的 IMP
    class_addMethod(kvoClass, sel, (IMP)kvoMethond, "v@:@");
    
    // 使用关联方法保存 observer(IMP方法内要用到这个对象)
    objc_setAssociatedObject(self, "dog_observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

vc code

    self.dog = [Dog new];
    self.dog.say = @"wow";
    NSLog(@"class name before kvo:%s",object_getClassName(self.dog));
//    [self.dog addObserver:self forKeyPath:@"say" options:NSKeyValueObservingOptionNew context:nil];
    [self.dog dog_addObserver:self forKeyPath:@"say" options:NSKeyValueObservingOptionNew context:nil];
    NSLog(@"class name after kvo:%s",object_getClassName(self.dog));
    self.dog.say = @"yeah";
    
}

- (void)dealloc{
    [self.dog removeObserver:self forKeyPath:@"setSay"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"%@",change);
}

相关文章

  • KVO的本质-另附runtime代码实现

    先上简单的背景代码: Log 可以注意到kvo之后对象dog的类型改变了。可以猜想设置了kvo之后:1.runti...

  • iOS 自定义KVO

    利用Runtime 实现简单的自定义kvo 代码githubgithub.com/zswj/custom-KVO ...

  • 透彻理解 KVO 观察者模式(附基于runtime实现代码)

    透彻理解 KVO 观察者模式(附基于runtime实现代码) 透彻理解 KVO 观察者模式(附基于runtime实...

  • KVO 的本质?

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

  • 面试题(2)

    runtime/KVO等面试题 1.KVO内部实现原则 回答:1>KVO是基于runtime机制实现的 2>当某个...

  • KVO原理分析

    一、KVO底层实现原理 示例代码: KVO 的实现过程实际上是利用了 OC 的 runtime 机制,当一个实例对...

  • ios-Runtime(运行时)

    利用runtime来实现归档解档 方法交换 俗称 OC的方法欺骗 KVO的实现原理 用runtime来实现KVO...

  • iOS_KVO本质解析

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

  • iOS - KVO

    ios用什么方式实现一个对象的kvo?(kvo的本质是什么?) 1.利用runtime的动态生成一个子类,并且让实...

  • iOS-底层原理(4)-KVO原理详解

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

网友评论

      本文标题:KVO的本质-另附runtime代码实现

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