KVO底层实现原理

原理:动态生成子类,重写set方法,分发observeValueForKeyPath消息

自定义KVO用到的一些runtime接口

//动态生成一个root类或者某类的派生类
objc_allocateClassPair(Class superclass, const char *name, 
                                         size_t extraBytes) 
//注册类
void objc_registerClassPair(Class cls) 
//给类添加一个方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, 
                                 const char *types) 

//修改对象的指针
Class object_setClass(id obj, Class cls) 

//添加关联属性
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

自定义KVO例子

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


@implementation NSObject (WHJKVO)
- (void)whj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
    // 创建类
    NSString * superClass = NSStringFromClass([self  class]);
    NSString *subClassName = [NSString stringWithFormat:@"whj_%@",superClass];
    const char *name = [subClassName UTF8String];
    Class myClass =  objc_allocateClassPair([self class], name, 0);
    // 注册类
    objc_registerClassPair(myClass);
    // 添加方法
    NSString *methodName = [NSString stringWithFormat:@"set%@:",[keyPath capitalizedString]];
    objc_setAssociatedObject(self, @"methodName", methodName, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //Person 添加关联属性
    class_addMethod(myClass, NSSelectorFromString(methodName), (IMP)myImp, "V@:@");

    //修改指针
    object_setClass(self, myClass);//执行这一行后self,就指向 whj_Person了
    // 添加关联对象,目的保存observer,以备给observer发送消息
    objc_setAssociatedObject(self, @"objc", observer, OBJC_ASSOCIATION_ASSIGN); //whj_Person 添加关联属性

}


void myImp(id self, SEL _cmd,id param) {

    //调用super 的实现
    //先保存子类
    id class = [self class];
    //指针改到父类
    object_setClass(self,class_getSuperclass(class));
    NSString *methodName = objc_getAssociatedObject(self, @"methodName"); // Person 获取关联属性
    //调用父类的方法
    objc_msgSend(self,NSSelectorFromString(methodName),param);

    // 发消息
    id observer =  objc_getAssociatedObject(self, @"objc"); // Person 获取关联属性
    objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),param,self,nil,nil);
    
    //指针改回来 ,这样下次属性变化还回来whj_Person类的myImp方法
    object_setClass(self, class);
    
}


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 KVO的用法前面已经讲过了,不懂得可以去看我的KVC、KVO探识(一),希望会对你有帮助。今天主要讲KVC、...
    Peak_One阅读 2,156评论 0 12
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,814评论 0 9
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,687评论 25 708
  • 文:星圻 凌晨1点临睡前,看到菜花小姐姐简书上的最新发布。没过两分钟,东城西久也发了一篇《我思考了两个月,决定放弃...
    星圻阅读 1,108评论 12 13
  • 生活的曲线是波动的,你时而处在浪尖上,只觉眼前豁然开朗,前途不可限量;时而又被惯性推入涛底,浑浑沌沌,满眼苍凉。 ...
    悠游四海阅读 220评论 0 2