小记--RACObserve(x, y.z)与RACObserve(x.y, z)

2017-01-30

在几个月前 JJD 和我说在使用RACObserve的时候发现这两种使用方法是有些许不同的,当时没有太在意,现在总算有时间去整理一下了。注:这里使用的ReactiveCocoa版本是 2.5 。

RACObserve是在开发中经常会使用到的宏定义,它的定义如下:

1
2
3
4
5
6
7
8
#define RACObserve(TARGET, KEYPATH) \
({ \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \
__weak id target_ = (TARGET); \
[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \
_Pragma("clang diagnostic pop") \
})

注:这里添加的observerself,在部分场合会遇到循环引用的问题,需要用到@weakify@strongify来解决。

文档里面说 observation 会持续到TARGETself被 deallocated ,其实这样就比较清楚了,RACObserve(x, y.z)RACObserve(x.y, z)停止观察的时机是有可能不同的。

我们从源码里看下 RAC 是如何做到的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(__weak NSObject *)observer {
return [[[self
rac_valuesAndChangesForKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:observer]
map:^(RACTuple *value) {
// -map: because it doesn't require the block trampoline that -reduceEach: uses
return value[0];
}]
setNameWithFormat:@"RACObserve(%@, %@)", self.rac_description, keyPath];
}
- (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)weakObserver {
NSObject *strongObserver = weakObserver;
keyPath = [keyPath copy];
NSRecursiveLock *objectLock = [[NSRecursiveLock alloc] init];
objectLock.name = @"org.reactivecocoa.ReactiveCocoa.NSObjectRACPropertySubscribing";
__weak NSObject *weakSelf = self;
RACSignal *deallocSignal = [[RACSignal
zip:@[
self.rac_willDeallocSignal,
strongObserver.rac_willDeallocSignal ?: [RACSignal never]
]]
doCompleted:^{
// Forces deallocation to wait if the object variables are currently
// being read on another thread.
[objectLock lock];
@onExit {
[objectLock unlock];
};
}];
return [[[RACSignal
createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
// do observe
}]
takeUntil:deallocSignal]
setNameWithFormat:@"%@ -rac_valueAndChangesForKeyPath: %@ options: %lu observer: %@", self.rac_description, keyPath, (unsigned long)options, strongObserver.rac_description];
}

我们可以看到,RAC 创建了一个deallocSignal来控制停止观察的时机,self对应的是方法的调用者,也就是target_,而strongObserver和 KVO 的observer是一样(在部分情况下observer有可能出现空的情况,所以需要做判断),只要self.rac_willDeallocSignalstrongObserver.rac_willDeallocSignal ?: [RACSignal never]有一个调用了sendComplete方法,observe 就会停止。

我们用一个demo来展示下这两种使用方式可能造成的区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- (void)observeTest {
self.person = [Person personWithName:@"WAMaker"];
[[[RACObserve(self, person.name)
skip:1]
distinctUntilChanged]
subscribeNext:^(NSString *name) {
NSLog(@"observe self ---------- name changed to: %@", name);
}
completed:^{
NSLog(@"observe self ---------- complete");
}];
[[[RACObserve(self.person, name)
skip:1]
distinctUntilChanged]
subscribeNext:^(NSString *name) {
NSLog(@"observe self.person --- name changed to: %@", name);
}
completed:^{
NSLog(@"observe self.person --- complete");
}];
self.person.name = @"Gemini";
self.person = nil;
self.person = [Person personWithName:@"WAMaker"];
}
// 控制台输出:
observe self.person --- name changed to: Gemini
observe self ---------- name changed to: Gemini
observe self ---------- name changed to: (null)
observe self ---------- name changed to: WAMaker
observe self.person --- complete

我们可以看到,使用RACObserve(self.person, name)self.person的值被修改后就停止了 observe ,而RACObserve(self, person.name)仍在继续。这个小区别得在以后开发的时候留个心眼了。