iOS

浅学-自定义KVO

2019-05-28  本文已影响0人  厦门_小灰灰

(1)KVO是基于runtime机制实现的,
(2)类A监听类B的一个属性的变化,当B被观察时,系统会在运行期动态创建一个B的子类NSKVONotifying_B,
(3)每个类都有一个isa指针指向当前类,B的isa指针会指向派生类NSKVONotifying_B,然后重写被观察的属性的setter方法,
(4)键值观察依赖于NSObject的两个方法willChangeValueForKey和didChangeValueForKey,在属性被改变之前调用willChangeValueForKey记录旧值,发生改变之后调用didChangeValueForKey用来储存新值,然后调用observeValueForKey:ofObject:change:context

仿照系统的方式进行实现,代码如下

创建一个NSObject的类别

.h文件

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (Extension)

- (void)lh_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

@end

NS_ASSUME_NONNULL_END

.m文件
#import "NSObject+Extension.h"
#import <objc/runtime.h>
#import <objc/message.h>

static const NSString *lh_getterName = @"lh_getterName";
static const NSString *lh_setterName = @"lh_setterName";
static const NSString *lh_observer = @"lh_observer";

@implementation NSObject (Extension)

- (void)lh_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
    
    //获取当前的类名
    NSString *oldClassName = NSStringFromClass([self class]);
    NSString *newClassName = [NSString stringWithFormat:@"LHKVONotifying_%@", oldClassName];
    
    //创建新的类
    Class cls = objc_getClass(newClassName.UTF8String);
    
    if ( !cls ) {
        cls = objc_allocateClassPair([self class], newClassName.UTF8String, 0);
        objc_registerClassPair(cls);
    }
    
    //set方法
    NSString *setFuncName = [NSString stringWithFormat:@"set%@:", [keyPath capitalizedString]];
    SEL setSel = NSSelectorFromString(setFuncName);
    class_addMethod(cls, setSel, (IMP)setter, "v@:@");
    
    //将isa指向改变
    object_setClass(self, cls);
    
    //关联对象
    objc_setAssociatedObject(self, &lh_observer, observer, OBJC_ASSOCIATION_ASSIGN);
    objc_setAssociatedObject(self, &lh_setterName, setFuncName, OBJC_ASSOCIATION_COPY_NONATOMIC);
    objc_setAssociatedObject(self, &lh_getterName, keyPath, OBJC_ASSOCIATION_COPY_NONATOMIC);
    
}

void setter(id self, SEL _cmd, id newValue) {
    
    NSString *setterName = objc_getAssociatedObject(self, &lh_setterName);
    NSString *getterName = objc_getAssociatedObject(self, &lh_getterName);
    
    //获取子类的类
    Class cls = [self class];
    
    //获取子类的父类, 并改变 isa 的指向
    object_setClass(self, class_getSuperclass(cls));
    
    //获取getter 的旧值
    id oldValue = ((id(*)(id, SEL))objc_msgSend)(self, NSSelectorFromString(getterName));
    
    //调用原类的set方法
    ((id(*)(id, SEL, id))objc_msgSend)(self, NSSelectorFromString(setterName), newValue);
    
    id observer = objc_getAssociatedObject(self, &lh_observer);
    
    NSMutableDictionary *change = @{}.mutableCopy;
    
    if ( oldValue ) {
        change[NSKeyValueChangeOldKey] = oldValue;
    }
    
    if ( newValue ) {
        change[NSKeyValueChangeNewKey] = newValue;
    }
    
    ((id(*)(id, SEL, id, id, id, id))objc_msgSend)(observer, @selector(observeValueForKeyPath:ofObject:change:context:), setterName, self, change, nil);
    
    //重新将isa指向子类
    object_setClass(self, cls);
}

@end


上一篇下一篇

猜你喜欢

热点阅读