RACKVO实现机制
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_];
});
}];