美文网首页iOS开发
KVO的使用及底层探究

KVO的使用及底层探究

作者: 姜益达 | 来源:发表于2019-06-22 17:20 被阅读0次

KVO的使用

KVO使用起来非常简单,三个步骤就搞定啦

1、通过addObserver: forKeyPath: options: context方法注册成为观察者,这样就可以观察到keyPath属性变化事件
2、实现observeValueForKeyPath: ofObject: change:context:方法,当属性值发生变化,KVO会回调这个方法通知观察者
3、当不需要监听的时候调用removeObserver: forKeyPath将KVO移除

KVO的触发模式

KVO有两种触发模式,手动和自动(不设置默认为自动)
来看看手动怎么触发

//在观察者的类中实现下面这个方法
// 1、模式调整 返回YES为自动 NO为手动
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
    if ([key isEqualToString:@"name"]) {
        return NO;
    }
    return YES;
}
//2、手动触发KVO
 [_p willChangeValueForKey:@"name"];
 _p.name = [NSString stringWithFormat:@"%d",a++];
 [_p didChangeValueForKey:@"name"];

手动触发的好处就是我们可以根据需求的不同来决定要不要触发KVO

KVO观察对象属性

假设我们有一个Dog类,类里面有age、level两个属性,那么我们怎么使用KVO观察对象呢?

//在观察者的类中实现下面这个类方法
+(NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
    NSSet* keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
    if ([key isEqualToString:@"dog"]) {
        keyPaths = [[NSSet alloc]initWithObjects:@"_dog.age",@"_dog.level", nil];
    }
    return keyPaths;
}

//注册观察者 观察dog属性
[_p addObserver:self forKeyPath:@"dog" options:NSKeyValueObservingOptionNew context:nil];

简直是 so easy 有木有啊!

不过,作为一名程序员,就要有打破砂锅问到底的精神,KVO究竟是怎么实现的呢?

下面一起来揭开KVO的神秘面纱,let's go

KVO原理探究

KVO的内部实现分以下三个步骤

1、创建一个子类(为什么KVO的实现使用继承而不使用分类?因为KVO会重写set方法,而使用分类重写set方法会覆盖掉原来类的set方法)
2、重写set方法
3、外界改变isa指针

-(void)JYC_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
    //1、创建一个类 父类为self即调用者
    NSString* oldClassName = NSStringFromClass([self class]);
    NSString* newClassName = [@"JYCkvo_" stringByAppendingString:oldClassName];
    Class myClass = objc_allocateClassPair(self.class, newClassName.UTF8String, 0);
    objc_registerClassPair(myClass);//注册类
    
    //2. 重写set方法(实际上添加了set方法,子类中没有没有父类的方法,仅仅是可以调用) myclass
    class_addMethod(myClass, @selector(setName:), (IMP)setName, "V@:@");
    
    //3、修改isa指针(将调用者指向子类),这样调用者调用set方法会来到本类中重写的set方法中
    object_setClass(self, myClass);
    
    //4、将观察者保存到当前对象  OBJC_ASSOCIATION_ASSIGN 属性类型是weak,防止循环引用
    objc_setAssociatedObject(self, "observer", observer, OBJC_ASSOCIATION_ASSIGN);
}

void setName(id self, SEL _cmd ,NSString* newname){
    NSLog(@"---%@",newname);
    //    调用父类的setName:方法
    Class class = [self class];
    object_setClass(self, class_getSuperclass(class));//当前类改为父类
    objc_msgSend(self, @selector(setName:),newname);
    
    //    拿到观察者
    id observer = objc_getAssociatedObject(self, "observer");
    if(observer){
        objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),@"name",self,@{@"new:":newname,@"kind:":@1},nil);
    }
    
    //改回子类
    object_setClass(self, class);
    
}
⚠️任何OC方法的调用本质上就是消息发送msgsend,发送的时候会包含两个隐式参数,分别是调用者和方法编号。

相关文章

  • KVO的使用及底层探究

    KVO的使用 KVO使用起来非常简单,三个步骤就搞定啦 1、通过addObserver: forKeyPath: ...

  • KVO底层探究

    一.什么是KVO KVO,即 Key-Value Observing 是 Objective-C 对观察者设计模式...

  • 2018-02-14

    探究KVO的底层实现原理 addObserver:forKeyPath:options:context:各个参数的...

  • 2.KVC-KVO基本使用及底层探究

    基础使用 KVC的使用 简单赋值 复杂赋值 修改私有变量 模型和字典的互相转换 取出多个模型中的某个属性的值 你以...

  • iOS开发面试攻略(KVO、KVC、多线程、锁、runloop、

    KVO & KVC KVO用法和底层原理 使用方法:添加观察者,然后怎样实现监听的代理 KVO底层使用了 isa-...

  • KVO 底层实现探究

    KVO概述 键值观察Key-Value-Observer就是观察者模式。 观察者模式的定义:一个目标对象管理所有依...

  • 探究KVO底层原理

     本文将会分成三部分,一是简述KVO的底层原理,二是详解系统的KVO,三是自己手动实现KVO,我们通过断点调试、N...

  • 底层原理

    iOS底层原理总结 - Category的本质 KVO详解及底层实现青少年一定要读的KVO指南 iOS 底层解析w...

  • iOS面试题整理

    1.探究KVO的底层实现原理 https://www.jianshu.com/p/829864680648 ·KV...

  • 自定义KVO

    导语: 如果对KVO原理不是很熟悉的,可以参考下简书另一篇文章《ios KVO原理探究》,主要是通过模拟KVO底层...

网友评论

    本文标题:KVO的使用及底层探究

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