用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被修改时,会提示被修改。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值