iOS进阶之runtime(2)--KVO简单模拟实现

前言:


上篇简单介绍了下runtime的几个API和KVO的底层原理,现在开始进入正题,来利用这几个API简单的模拟下KVO的原理.大概的步骤就是.

1.在注册KVO的时候注册一个通知,并且替换set方法.

2.然后在我们的set方法里调用原有的set方法,好继续执行原有set方法的逻辑,比如赋值等.

3.然后在我们替换的set方法里判断值是否发生变化,如果发生变化就发送一个通知执行我们特定的观察者方法.

好了不多说,直接贴代码. demo下载请戳我

- (void)zxp_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
    //进行KVO监听的时候,注册一个通知
    [[NSNotificationCenter defaultCenter] addObserver:observer
                                             selector:@selector(sendNotification:)
                                                 name:[self notificationNameWithkeyPath:keyPath]
                                               object:nil];
    
//设置关联,,把需要监听的KVO属性(path)保存起来
    objc_setAssociatedObject(self, @selector(getKeyPathString), keyPath, OBJC_ASSOCIATION_COPY_NONATOMIC);
    objc_setAssociatedObject(self, @selector(getKeyValueOptions), @(options), OBJC_ASSOCIATION_ASSIGN);
    //得要这个监听属性的set方法
    NSString *setMethodString = [NSString stringWithFormat:@"set%@%@:",[keyPath substringToIndex:1].uppercaseString,[keyPath substringFromIndex:1]];
    //把 原有的set方法替换成我们的set方法
    method_exchangeImplementations(class_getInstanceMethod([self class], NSSelectorFromString(setMethodString)), class_getInstanceMethod([NSObject class], @selector(newSetMethod:)));

}
//移除观察者
- (void)zxp_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
    [[NSNotificationCenter defaultCenter] removeObserver:observer
                                                    name:[self notificationNameWithkeyPath:keyPath]
                                                  object:observer];
}
//观察者的值发生变化的时候执行,需要监听的对象重写此方法,否则抛出异常
- (void)zxp_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    NSAssert(NO, @"需要实现方法:%@",NSStringFromSelector(@selector(zxp_observeValueForKeyPath:ofObject:change:context:)));
}

#pragma mark - private

- (NSString *)notificationNameWithkeyPath:(NSString *)keyPath {
    return [NSString stringWithFormat:@"%zi,%@,%@",self,NSStringFromSelector(@selector(notificationNameWithkeyPath:)),keyPath];
}
//我们自己实现的set方法,在set方法里判断监听的属性是否发生变化,如果发生变化就发送一个通知
- (void)newSetMethod:(id)params {
    id value = [self valueForKeyPath:[self getKeyPathString]];
    if (value != params && [self getKeyValueOptions] == NSKeyValueObservingOptionNew) {
        [[NSNotificationCenter defaultCenter] postNotificationName:[self notificationNameWithkeyPath:[self getKeyPathString]] object:nil];
    }
    [self newSetMethod:params];
}

- (NSString *)getKeyPathString {
    return objc_getAssociatedObject(self, @selector(getKeyPathString));
}

- (NSKeyValueObservingOptions)getKeyValueOptions {
    return [objc_getAssociatedObject(self, @selector(getKeyValueOptions)) integerValue];
}

#pragma mark - notification methods
//通知的方法,如果收到此通知就执行zxp_observeValueForKeyPath:ofObject: change: context: 方法                                           
 
 
 
 
 
 
- (void)sendNotification:(NSNotification *)notification {
#warning 没做传参,随便用什么保存这几个参数存进来就行了,比如AssociatedObject里
    [self zxp_observeValueForKeyPath:nil ofObject:nil change:nil context:nil];
}


转载于:https://www.cnblogs.com/zhangxiaoping/p/5146656.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值