iOS开发技术

RACKVO实现机制

2018-07-12  本文已影响8人  我是小胡胡分胡
image.png

KVO原理

当一个object有观察者时,动态创建这个object的类的子类
对于每个被观察的property,重写其set方法
在重写的set方法中调用- willChangeValueForKey:和- didChangeValueForKey:通知观察者
当一个property没有观察者时,删除重写的方法
当没有observer观察任何一个property时,删除动态创建的子类

KVO跟load方法中的method-swizzle 有一个显著的区别:
kvo只是针对当前类的实例,做的isa-swizzle
而Load方法中方法交换,是针对所有类的。 每一个类的实例都被影响。

从RACObserve到最终的系统调用KVO

有一个关键角色,就是Obj, 在哪个类中的调用, 就会取self传进去。其目的是:

如果target 和 self 哪一个释放了, kvo自动移除。

从系统的KVO回到信号发送的起点。

被观察对象是target,
观察的属性是keypath,
观察者是Obj(也就是调用observe宏的类的当前self),也就是老板。
而真正的观察者,是RACProxy,他是一个单例对象,他才是干活的。

所有通知消息,由Proxy转发给Tranmpoline。
Tranmpoline也是存储在Proxy的NSMapTable中。
而Tranmpoline中存储了(target,keypath 和block)

通过, RACPropertySubscribing也就是NSObject的扩展的方法中, 创建信号 和 block挂钩的。
Tranmpoline 中的block的调用,最终触发了信号的发送。

问题来了:

从图中看出, Obj的传送过程中,
(__weak NSObject *)observer
(__weak NSObject *)weakObserver
@property (nonatomic, readonly, weak) NSObject *observer;

全部都是用weak持有的, 为什么能导致内存泄漏呢?

原因很简单。就是weak得到对象的时机。

假如我们这么修改RACObserve的宏定义

#define _RACObserve(TARGET, KEYPATH) \
({ \
__weak id target_ = (TARGET); \
__weak id Self_ = self;\
[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer: Self_]; \
})




预处理替换后,代码为

    self.flattenMapSignal = [signal flattenMap:^__kindof RACSignal * _Nullable(MTModel*model) {

 return ({ 
    __attribute__((objc_ownership(weak))) id target_ = (model);

//这里的self在赋值前,仍然被block捕获了
    __attribute__((objc_ownership(weak))) id Self_ = self;

    [target_ rac_valuesForKeyPath:@(((void)(__objc_no && ((void)model.title, __objc_no)), "title")) observer: Self_]; 
    });
    }];

上一篇下一篇

猜你喜欢

热点阅读