Contents
  1. 1. 前言
  2. 2. 正题

前言

static int a = 0;
[[RACObserve(self, number) distinctUntilChanged] subscribeNext:^(id x) {
    a++;
    printf("RAC a is %d\n", a);
}];

self.number = @1;
self.number = @2;

这段代码运行之后 a 的值是 3,也就是 subscribeNext 会回调 3 次。

正题

static int a = 0;
[[RACObserve(self, array) distinctUntilChanged] subscribeNext:^(id x) {
    a++;
    printf("RAC a is %d\n", a);
}];

NSMutableArray *obj = [@[@1, @2] mutableCopy];
NSMutableArray *another = [@[@1, @2, @3] mutableCopy];

self.array = obj;
[another removeLastObject];
self.array = another;

如果换成这样呢?
不同之处在于倒数第二行对 another 进行了修改。
结果是 2,也就是说最后一行的赋值并没有发出消息。

原因在于 distinctUntilChanged 的实现有针对上一个值做比较,如果 isEqual 成立尽管指针不一样消息还是不会发送的。

- (__kindof RACStream *)distinctUntilChanged {
    Class class = self.class;

    return [[self bind:^{
        __block id lastValue = nil;
        __block BOOL initial = YES;

        return ^(id x, BOOL *stop) {
            if (!initial && (lastValue == x || [x isEqual:lastValue])) return [class empty];

            initial = NO;
            lastValue = x;
            return [class return:x];
        };
    }] setNameWithFormat:@"[%@] -distinctUntilChanged", self.name];
}

但是呢,代码里比较容易出现上面的例子,NSMutableArray 可以换成任意 mutable 的容器以及任意自定义对象,只要你的自定义对象自己实现了 isEqual 就可能造成这种结局。

大多数情况下我们的 isEqual 不会比较指针不一样就直接 return NO。

所以在 RACObserve 对象并希望 distinctUntilChanged 能够工作正常的话要非常小心

  1. 对象尽量是 “immutable”
  2. 如果不是 immutable,就要非常小心搭配使用 distinctUntilChanged,如果中途改了对象的 property 很有可能破坏自己的预期

真实世界中很难预防有人写出这样的代码。很大程度上,observe 的动作和改 property 以及更新对象触发回调这三处代码相互隔离非常远,不在一个文件里。写的人摸不清,维护的人更麻烦。

ReactiveCocoa 很难用理由 +1

Contents
  1. 1. 前言
  2. 2. 正题