天天看点

用Runtime实现KVO

一.创建一个继承自NSObject的类目GXJKVO,在.h文件中添加两个方法

//添加观察者

- (void)addObserver:(NSObject *)observer forKey:(NSString *)key withBlock:(void(^)(id observed, NSString *key, id oldValue, id newValue))block;

//删除观察者

  • (void)removeObserver:(NSObject *)observer forKey:(NSString *)key;

二.在.m中实现这两个方法

1.首先声明两个唯一的key

NSString *const kClassPrefix = @"GXJKVOClassPrefix";

NSString *const kGXJKVOAssociateKey = @“GXJKVOObserverArrayKey";

2.添加一个新类的延展

@interface ObserveInfo : NSObject

@property (strong, nonatomic) id observer;

@property (copy , nonatomic) NSString * key;

@property (copy , nonatomic) void(^observerBlock) (id observerObject, NSString * key, id oldValue, id newValue);

+(id)instanceWithObserver:(id)observer forKey:(NSString *)key block:(void (^)(id, NSString *, id, id))block;

@end

@implementation ObserveInfo

+(id)instanceWithObserver:(id)observer forKey:(NSString *)key block:(void (^)(id, NSString *, id, id))block

{

    ObserveInfo *observerInfo = [[ObserveInfo alloc]init];

    if (observerInfo) {

        observerInfo.observer = observer;

        observerInfo.key = key;

        observerInfo.observerBlock = block;

    }

    return observer;

}

@end

3.实现添加观察者、删除观察者方法

//函数 通过key获得setter方法名

NSString *getSetterName(NSString *key) {

    NSString *firstCharacter = [key substringFromIndex:1];

    return [NSString stringWithFormat:@"set%@%@",[firstCharacter uppercaseString],[key substringFromIndex:1]];

}

//函数 实现通过setter方法名取出key

NSString *getKey(NSString *setName) {

    //去掉冒号

    NSString *preName = [setName substringToIndex:setName.length - 1];

    //去掉set

    NSString * name = [preName substringFromIndex:3];

    //获得首字母

    NSString * firstCharacter = [name substringToIndex:1];

    //返回字符串

    return [NSString stringWithFormat:@"%@%@",[firstCharacter lowercaseString],[name substringFromIndex:1]];

}

//生成衍生类方法

- (Class)makeKVOClass:(Class)originClass

{

    NSString *className = NSStringFromClass(originClass);

    NSString *kvoClassName = [NSString stringWithFormat:@"%@%@",kClassPrefix,className];

    Class kvoClass = objc_allocateClassPair(object_getClass(self), kvoClassName.UTF8String, 0);

    objc_registerClassPair(kvoClass);

    return kvoClass;

}

//判断衍生类是否已经实现当前key所对应的setter方法

- (BOOL)hasSelector:(SEL)aSelector

{

    //获取方法链表并遍历

    unsigned int methodCount = 0;

    //获取方法链表

    Method *methodArray = class_copyMethodList(object_getClass(self), &methodCount);

    //遍历每个方法名

    for (int i = 0; i < methodCount; i++) {

        Method m = methodArray[i];

        if (method_getName(m) == aSelector) {

            free(methodArray);

            return YES;

        }

    }

    //否则返回NO

    free(methodArray);

    return NO;

}

//gxj_kvoSetter完成两个功能,1通过向父类发消息完成set本来的赋值功能,2并行调用观察者方法

void gxj_kvoSetter(id objc_self, SEL objc_cmd, id newValue) {

    //获取当前的setter方法名

    NSString *setterName = NSStringFromSelector(objc_cmd);

    //获得key

    NSString *key = getKey(setterName);

    //获得oldValue

    id oldValue = [objc_self valueForKey:key];

    //objc_msgSendSuper()函数的参数是父类结构体,需要手动创建

    struct objc_super selfSuper = {

        .receiver = objc_self,

        .super_class = class_getSuperclass(object_getClass(objc_self))

    };

    objc_msgSendSuper(&selfSuper, objc_cmd,newValue);

    //并行回调观察者block

    NSMutableArray *observeArray = objc_getAssociatedObject(objc_self, (__bridge const void *)kGXJKVOAssociateKey);

    //遍历数组进行回调

    for (ObserveInfo *info in observeArray) {

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

            info.observerBlock(objc_self, key, oldValue, newValue);

        });

    }

}

//添加观察者

- (void)addObserver:(NSObject *)observer forKey:(NSString *)key withBlock:(void(^)(id observed, NSString *key, id oldValue, id newValue))block

{

    //判断能否进行KVO,是否存在此key对应的属性setter方法

    NSString *setterName = getSetterName(key);

    Method setMethod = class_getInstanceMethod(object_getClass(self), NSSelectorFromString(setterName));

    if (!setMethod) {

        NSLog(@"无法KVO");

        return;

    }

    //是否已经生成衍生类

    NSString *className = NSStringFromClass(object_getClass(self));

    Class kvoClass = object_getClass(self);

    if (![className hasPrefix:kClassPrefix]) {

        //生成当前类的衍生类

        kvoClass = [self makeKVOClass:object_getClass(self)];

        //生成衍生类后,改变当前的类的类标识,使self变成kvoClass的实例

        object_setClass(self, kvoClass);

    }

    //判断衍生类是否已经实现当前key所对应的setter方法

    if (![self hasSelector:NSSelectorFromString(setterName)]) {

        //若未实现setter方法,则添加我们实现的kvoSetter

    }

    //完成以上,就可以将观察者添加到关联数组中

    NSMutableArray *observerArray = objc_getAssociatedObject(self, (__bridge const void *)kGXJKVOAssociateKey);

    ObserveInfo *observerInfo = [ObserveInfo instanceWithObserver:observer forKey:key block:block];

    if (!observerArray) {

        observerArray = [NSMutableArray array];

        objc_setAssociatedObject(self, (__bridge const void *)(kGXJKVOAssociateKey), observerArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    }

    [observerArray addObject:observerInfo];

}

//删除观察者

- (void)removeObserver:(NSObject *)observer forKey:(NSString *)key

{

    NSMutableArray *observerArray = objc_getAssociatedObject(self, (__bridge const void *)kGXJKVOAssociateKey);

    for (ObserveInfo * observerInfo in observerArray) {

        if (observerInfo.observer == observer && [observerInfo.key isEqualToString:key]) {

            [observerArray removeObject:observerInfo];

        }

    }

}

三.在ViewController中调用添加观察者方法

//创建一个model,添加一个name属性

Company *p1 = [Company new];

    [p1 addObserver:self forKey:@"name" withBlock:^(id observed, NSString *key, id oldValue, id newValue) {

        NSLog(@"object = %@, key = %@, oldValue = %@, newValue = %@",observed,key,oldValue,newValue);

    }];

    p1.name = @"Baidu";

    p1.name = @"Tencent";

    p1.name = @“Google";

当p1的属性name被修改时,会提示被修改。

继续阅读