美文网首页
KVO简单使用及其实现原理

KVO简单使用及其实现原理

作者: oceanLong | 来源:发表于2018-08-20 20:08 被阅读13次

前言

在应用开发中,我们经常需要使用到,观察者模式,能监听某些对象属性的变化,进行相应的操作。在iOS中,OC为我们提供了一套更加简洁优雅的观察方式——KVO。

KVO全称Key Value Observing,键值监听机制,由NSKeyValueObserving协议提供支持,NSObject类继承了该协议,所以NSObject的子类都可使用该方法。

KVO的使用

KVO的使用非常简单,我们只需要给对象的指定属性绑定监听,并设置监听类型。每当我们对指定的属性进行读写操作时,就会,OC就会调用observeValueForKeyPath方法。我们在observeValueForKeyPath中进行判断,确定是哪个对象的事件。

    Person *person = [Person alloc]int];
    /*
     作用:给对象绑定一个监听器(观察者)
     - Observer 观察者
     - KeyPath 要监听的属性
     - options 选项(方便在监听的方法中拿到属性值)
     */
    [person addObserver:person forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

    // 移除监听
    [person removeObserver:person forKeyPath:@"name"];

    /**
     *  当监听的属性值发生改变
     *
     *  @param keyPath 要改变的属性
     *  @param object  要改变的属性所属的对象
     *  @param change  改变的内容
     *  @param context 上下文
     */
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
        NSLog(@"%@------%@------%@", keyPath, object, change);
    }

在上面的代码中,我们对Personname属性进行监听。当我们读写name时,系统就会调用observeValueForKeyPath方法了。

KVO的原理概述

那么,系统是如何实现KVO的呢?
其实,每当我们使用KVO的addObserver时,系统会默默地创建一个类。我们估且把它叫作:KVO_Person。然后,系统动态地让其继承Person类,并添加方法:setNamegetName。在KVO_PersonsetNamegetName方法实现中,添加observeValueForKeyPath的方法的调用。

此时,我们如果调用KVO_PersonsetNamegetName,则会调用observeValueForKeyPath

但是,在KVO的使用中,我们对KVO_Person是完全不知情的。那么,我们如何调用它的setNamegetName呢?其实,在创建KVO_Person时,我们就将Personisa指针,换成了KVO_Person

Runtime一瞥

isa是什么呢?这里涉及到iOS的Runtime知识。iOS的Runtime博大精深,在此只讲其中一小部分。

在iOS中,所有的方法调用,都是以发消息的方式进行的。

我们先来了解一下OC中类的结构

//对象
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};
//类
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
//方法列表
struct objc_method_list {
    struct objc_method_list *obsolete                        OBJC2_UNAVAILABLE;
    int method_count                                         OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]                        OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;
//方法
struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types                                       OBJC2_UNAVAILABLE;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}

一个类接收到消息后,处理的流程是:

  1. 系统首先找到消息的接收对象,然后通过对象的isa找到它的类。
  2. 在它的类中查找method_list,是否有selector方法。
  3. 没有则查找父类的method_list。
  4. 找到对应的method,执行它的IMP。
  5. 转发IMP的return值。

所以,我们知道,当我们替换掉isa时,其实就是替换掉了消息的处理类。在上面的例子中,当我们调用addObserver时,其实就是将Person的isa替换为KVO_Person

手动实现一个小小的KVO

最后,让我们自己来实现一个小小的KVO,帮助读者理解iOS的Runtime特性和KVO的实现原理。

#import "ViewController.h"
#include <objc/runtime.h>
#import "Person.h"


@interface ViewController ()

@property(nonatomic, strong) UIButton* button;

@end

@implementation ViewController


- (void)viewDidLoad {
    [super viewDidLoad];
    self.button = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
    [_button setTitle:@"test" forState:UIControlStateNormal];
    [_button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [_button addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_button];
}

- (void)click{
    
    
    Person *person = [[Person alloc]init];
//-------------------------------------------------------------------
    // 创建类 KVO_Person
    Class kvo_person = objc_allocateClassPair([Person class], "KVO_Person", 0);
    // 添加属性
    objc_property_attribute_t type = { "T", "@\"NSString\"" };
    objc_property_attribute_t ownership = { "C", "" }; // C = copy
    objc_property_attribute_t backingivar  = { "V", "_privateName" };
    objc_property_attribute_t attrs[] = { type, ownership, backingivar };
    class_addProperty(kvo_person, "name", attrs, 3);
    // 添加方法
    class_addMethod(kvo_person, @selector(setName:), (IMP)nameSetter, "v@:@");
    // 注册该类
    objc_registerClassPair(kvo_person);
    // 替换 isa
    object_setClass(person, kvo_person);
//-------------------------------------------------------------------
    [person setName:@"test"];
}

//set方法
void nameSetter(id self, SEL _cmd, NSString *newName) {
    NSLog(@" -------------------- Person set start ");
    Ivar ivar = class_getInstanceVariable([self class], "_privateName");
    id oldName = object_getIvar(self, ivar);
    if (oldName != newName) object_setIvar(self, ivar, [newName copy]);
    NSLog(@" -------------------- Person set end ");
}

@end

以上就是KVO的简单使用及其实现原理。

如有问题,欢迎指正。

相关文章

  • KVO简单使用及其实现原理

    前言 在应用开发中,我们经常需要使用到,观察者模式,能监听某些对象属性的变化,进行相应的操作。在iOS中,OC为我...

  • iOS高级进阶之KVO

    KVO的原理 分析原理 使用 手动调用 自己实现KVO NSObject+KVOBlock.h NSObject+...

  • iOS原理篇(一): KVO实现原理

    KVO实现原理 什么是 KVO KVO 基本使用 KVO 的本质 总结 一 、 什么是KVO KVO(Key-Va...

  • iOS - 自定义KVO

    之前我们已经了解过了KVO的底层实现原理,不过呢,在我们开始实现自定义KVO之前再来简单回顾下KVO的实现原理 1...

  • KVO基本使用

    分三部分解释KVO一.KVO基本使用二.KVO原理解析三.自定义实现KVO 一、KVO基本使用 使用KVO,能够非...

  • iOS探索KVO实现原理,重写KVO

    写响应式编程博客时,提到了KVO,今天我们探索一下KVO的实现原理及如何自己实现KVO功能 首先简单的KVO实现 ...

  • KVO和KVC的理解

    KVO键值监听的使用 KVO是OC观察者模式的又一实现,使用了isa混写来实现的KVO KVO原理 1.运行时会创...

  • KVO底层原理分析

    一、 KVO内部实现原理 KVO是基于 runtime机制实现的,使用了isa 混写(isa-swizzling)...

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

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

  • 底层原理探究(一)KVO

    问题:1、KVO的使用?实现原理?(为什么要创建子类来实现)2、KVC的使用?实现原理?(KVC拿到key以后,是...

网友评论

      本文标题:KVO简单使用及其实现原理

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