ReactiveObjC 源码阅读笔记 (RACObserve())

常用KVO宏 RACObserve(<#TARGET#>, <#KEYPATH#>)

使用:

    [[RACObserve(self.viewModel, model.number) takeUntil:self.rac_willDeallocSignal] subscribeNext:^(id  _Nullable x) {
            // 响应监听
    }];

源码

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

#if __clang__ && (__clang_major__ >= 8)
#define RACObserve(TARGET, KEYPATH) _RACObserve(TARGET, KEYPATH)
#else
#define RACObserve(TARGET, KEYPATH) \
({ \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \
    _RACObserve(TARGET, KEYPATH) \
    _Pragma("clang diagnostic pop") \
})
#endif
  • NSObject (RACPropertySubscribing) 创建订阅信号
- (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];
        }]
        // 从 RacStream 继承来的 name 属性赋值,只 debug 时用。
        setNameWithFormat:@"RACObserve(%@, %@)", RACDescription(self), 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.ReactiveObjC.NSObjectRACPropertySubscribing";

    __weak NSObject *weakSelf = self;

    // 创建dealloc信号,当 self 或 observer 被释放时,取消监听
    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 subscriber) {
            // Hold onto the lock the whole time we're setting up the KVO
            // observation, because any resurrection that might be caused by our
            // retaining below must be balanced out by the time -dealloc returns
            // (if another thread is waiting on the lock above).
            [objectLock lock];
            @onExit {
                [objectLock unlock];
            };

            __strong NSObject *observer __attribute__((objc_precise_lifetime)) = weakObserver;
            __strong NSObject *self __attribute__((objc_precise_lifetime)) = weakSelf;

            if (self == nil) {
                // self 为空时,sendCompleted,结束信号
                [subscriber sendCompleted];
                return nil;
            }
            // 调用 NSObject (RACKVOWrapper) 中的方法,按keyPath逐级添加监听
            return [self rac_observeKeyPath:keyPath options:options observer:observer block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
                // 收到监听回调,调用 sendNext 触发信号
                [subscriber sendNext:RACTuplePack(value, change)];
            }];
        }]
        takeUntil:deallocSignal]
        setNameWithFormat:@"%@ -rac_valueAndChangesForKeyPath: %@ options: %lu observer: %@", RACDescription(self), keyPath, (unsigned long)options, RACDescription(strongObserver)];
}
  • NSObject (RACKVOWrapper) 对keyPath各结点进行逐级监听
- (RACDisposable *)rac_observeKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)weakObserver block:(void (^)(id, NSDictionary *, BOOL, BOOL))block {
    NSCParameterAssert(block != nil);
    NSCParameterAssert(keyPath.rac_keyPathComponents.count > 0);

    keyPath = [keyPath copy];

    NSObject *strongObserver = weakObserver;
    // 以```.```分隔,拆分keyPath
    NSArray *keyPathComponents = keyPath.rac_keyPathComponents;
    // keyPath是否只有一个结点
    BOOL keyPathHasOneComponent = (keyPathComponents.count == 1);
    // keyPath 头部结点
    NSString *keyPathHead = keyPathComponents[0];
    // keyPath 头部结点以外的结点
    NSString *keyPathTail = keyPath.rac_keyPathByDeletingFirstKeyPathComponent;

    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];

    // The disposable that groups all disposal necessary to clean up the callbacks
    // added to the value of the first key path component.
    RACSerialDisposable *firstComponentSerialDisposable = [RACSerialDisposable serialDisposableWithDisposable:[RACCompoundDisposable compoundDisposable]];
    RACCompoundDisposable * (^firstComponentDisposable)(void) = ^{
        return (RACCompoundDisposable *)firstComponentSerialDisposable.disposable;
    };

    [disposable addDisposable:firstComponentSerialDisposable];
    // 是否需要添加 property dealloc 时的监听,用来对象被释放时,解除订阅
    BOOL shouldAddDeallocObserver = NO;
    // runtime 获取 keyPathHead 对应的属性
    objc_property_t property = class_getProperty(object_getClass(self), keyPathHead.UTF8String);
    if (property != NULL) {
        rac_propertyAttributes *attributes = rac_copyPropertyAttributes(property);
        if (attributes != NULL) {
            @onExit {
                free(attributes);
            };
            BOOL isObject = attributes->objectClass != nil || strstr(attributes->type, @encode(id)) == attributes->type;
            BOOL isProtocol = attributes->objectClass == NSClassFromString(@"Protocol");
            BOOL isBlock = strcmp(attributes->type, @encode(void(^)(void))) == 0;
            BOOL isWeak = attributes->weak;
            /**
             判断是否需要对 property 的 dealloc 添加监听
             是一个对象,并被弱引用,不是block,不是protocol
             不是实例对象,不需要添加。未被弱引用,不会被自动释放,也不需要添加
            */
            shouldAddDeallocObserver = isObject && isWeak && !isBlock && !isProtocol;
        }
    }
    // clean up the callback to the firstComponentDisposable.
    void (^addDeallocObserverToPropertyValue)(NSObject *) = ^(NSObject *value) {
        if (!shouldAddDeallocObserver) return;

        // If a key path value is the observer, commonly when a key path begins
        // with "self", we prevent deallocation triggered callbacks for any such key
        // path components. Thus, the observer's deallocation is not considered a
        // change to the key path.
        if (value == weakObserver) return;

        NSDictionary *change = @{
            NSKeyValueChangeKindKey: @(NSKeyValueChangeSetting),
            NSKeyValueChangeNewKey: NSNull.null,
        };

        RACCompoundDisposable *valueDisposable = value.rac_deallocDisposable;
        RACDisposable *deallocDisposable = [RACDisposable disposableWithBlock:^{
            block(nil, change, YES, keyPathHasOneComponent);
        }];

        [valueDisposable addDisposable:deallocDisposable];
        [firstComponentDisposable() addDisposable:[RACDisposable disposableWithBlock:^{
            [valueDisposable removeDisposable:deallocDisposable];
        }]];
    };

    // Adds the callback block to the remaining path components on the value. Also
    // adds the logic to clean up the callbacks to the firstComponentDisposable.
    void (^addObserverToValue)(NSObject *) = ^(NSObject *value) {
        // 递归,对 keyPathTail 各结点添加监听
        RACDisposable *observerDisposable = [value rac_observeKeyPath:keyPathTail options:(options & ~NSKeyValueObservingOptionInitial) observer:weakObserver block:block];
        [firstComponentDisposable() addDisposable:observerDisposable];
    };
    // Observe only the first key path component, when the value changes c
lean up
    // the callbacks on the old value, add callbacks to the new value and call the
    // callback block as needed.
    //
    // Note this does not use NSKeyValueObservingOptionInitial so this only
    // handles changes to the value, callbacks to the initial value must be added
    // separately.
    NSKeyValueObservingOptions trampolineOptions = (options | NSKeyValueObservingOptionPrior) & ~NSKeyValueObservingOptionInitial;
    // 创建 RACKVOTrampoline,管理监听
    RACKVOTrampoline *trampoline = [[RACKVOTrampoline alloc] initWithTarget:self observer:strongObserver keyPath:keyPathHead options:trampolineOptions block:^(id trampolineTarget, id trampolineObserver, NSDictionary *change) {
        // If this is a prior notification, clean up all the callbacks added to the
        // previous value and call the callback block. Everything else is deferred
        // until after we get the notification after the change.
        if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
            [firstComponentDisposable() dispose];

            if ((options & NSKeyValueObservingOptionPrior) != 0) {
                block([trampolineTarget valueForKeyPath:keyPath], change, NO, keyPathHasOneComponent);
            }

            return;
        }

        // From here the notification is not prior.
        // 通过 KVC 取值
        NSObject *value = [trampolineTarget valueForKey:keyPathHead];

        // If the value has changed but is nil, there is no need to add callbacks to
        // it, just call the callback block.
        if (value == nil) {
            block(nil, change, NO, keyPathHasOneComponent);
            return;
        }

        // From here the notification is not prior and the value is not nil.

        // Create a new firstComponentDisposable while getting rid of the old one at
        // the same time, in case this is being called concurrently.
        RACDisposable *oldFirstComponentDisposable = [firstComponentSerialDisposable swapInDisposable:[RACCompoundDisposable compoundDisposable]];
        [oldFirstComponentDisposable dispose];

        addDeallocObserverToPropertyValue(value);

        // If there are no further key path components, there is no need to add the
        // other callbacks, just call the callback block with the value itself.
        if (keyPathHasOneComponent) {
            block(value, change, NO, keyPathHasOneComponent);
            return;
        }

        // The value has changed, is not nil, and there are more key path components
        // to consider. Add the callbacks to the value for the remaining key path
        // components and call the callback block with the current value of the full
        // key path.
        // 执行 addObserverToValue block,对 keyPath 上其他结点添加监听
        addObserverToValue(value);
        block([value valueForKeyPath:keyPathTail], change, NO, keyPathHasOneComponent);
    }];

    // Stop the KVO observation when this one is disposed of.
    [disposable addDisposable:trampoline];

    // Add the callbacks to the initial value if needed.
    NSObject *value = [self valueForKey:keyPathHead];
    if (value != nil) {
        addDeallocObserverToPropertyValue(value);

        if (!keyPathHasOneComponent) {
            addObserverToValue(value);
        }
    }

    // Call the block with the initial value if needed.
    if ((options & NSKeyValueObservingOptionInitial) != 0) {
        id initialValue = [self valueForKeyPath:keyPath];
        NSDictionary *initialChange = @{
            NSKeyValueChangeKindKey: @(NSKeyValueChangeSetting),
            NSKeyValueChangeNewKey: initialValue ?: NSNull.null,
        };
        block(initialValue, initialChange, NO, keyPathHasOneComponent);
    }


    RACCompoundDisposable *observerDisposable = strongObserver.rac_deallocDisposable;
    RACCompoundDisposable *selfDisposable = self.rac_deallocDisposable;
    // Dispose of this observation if the receiver or the observer deallocate.
    [observerDisposable addDisposable:disposable];
    [selfDisposable addDisposable:disposable];

    return [RACDisposable disposableWithBlock:^{
        [disposable dispose];
        [observerDisposable removeDisposable:disposable];
        [selfDisposable removeDisposable:disposable];
    }];
}
  • RACKVOTrampoline 原生KVO管理
- (instancetype)initWithTarget:(__weak NSObject *)target observer:(__weak NSObject *)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block {
    NSCParameterAssert(keyPath != nil);
    NSCParameterAssert(block != nil);

    NSObject *strongTarget = target;
    if (strongTarget == nil) return nil;

    self = [super init];
    // 保存相当参数
    _keyPath = [keyPath copy];

    _block = [block copy];
    _weakTarget = target;
    _unsafeTarget = strongTarget;
    _observer = observer;
    /**
     使用 RACKVOProxy 进行消息转发。
     self 作为context的值,用 context 保存ovserver,回调时通过 context 获取 observer,来调用对应 observer 的 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 方法。
    */
    [RACKVOProxy.sharedProxy addObserver:self forContext:(__bridge void *)self];
    [strongTarget addObserver:RACKVOProxy.sharedProxy forKeyPath:self.keyPath options:options context:(__bridge void *)self];

    [strongTarget.rac_deallocDisposable addDisposable:self];
    [self.observer.rac_deallocDisposable addDisposable:self];

    return self;
}

- (void)dealloc {
    [self dispose];
}

#pragma mark Observation
// 信号结束或取消订阅时的清理
- (void)dispose {
    NSObject *target;
    NSObject *observer;

    @synchronized (self) {
        _block = nil;

        // The target should still exist at this point, because we still need to
        // tear down its KVO observation. Therefore, we can use the unsafe
        // reference (and need to, because the weak one will have been zeroed by
        // now).
        target = self.unsafeTarget;
        observer = self.observer;

        _unsafeTarget = nil;
        _observer = nil;
    }

    [target.rac_deallocDisposable removeDisposable:self];
    [observer.rac_deallocDisposable removeDisposable:self];
    // 解除监听
    [target removeObserver:RACKVOProxy.sharedProxy forKeyPath:self.keyPath context:(__bridge void *)self];
    // 删除监听者
    [RACKVOProxy.sharedProxy removeObserver:self forContext:(__bridge void *)self];
}

// 消息被转发回来后,调用 block 回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (context != (__bridge void *)self) {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        return;
    }

    RACKVOBlock block;
    id observer;
    id target;

    @synchronized (self) {
        block = self.block;
        observer = self.observer;
        target = self.weakTarget;
    }

    if (block == nil || target == nil) return;

    block(target, observer, change);
}
  • RACKVOProxy 用 KVO 统一管理
+ (instancetype)sharedProxy {
    static RACKVOProxy *proxy;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        proxy = [[self alloc] init];
    });
    return proxy;
}

- (instancetype)init {
    self = [super init];

    _queue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACKVOProxy", DISPATCH_QUEUE_SERIAL);
    // 创建表,保存真正的 observer
    _trampolines = [NSMapTable strongToWeakObjectsMapTable];

    return self;
}

- (void)addObserver:(__weak NSObject *)observer forContext:(void *)context {
    NSValue *valueContext = [NSValue valueWithPointer:context];

    dispatch_sync(self.queue, ^{
        [self.trampolines setObject:observer forKey:valueContext];
    });
}

- (void)removeObserver:(NSObject *)observer forContext:(void *)context {
    NSValue *valueContext = [NSValue valueWithPointer:context];

    dispatch_sync(self.queue, ^{
        [self.trampolines removeObjectForKey:valueContext];
    });
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    // 获取真正的 observer,调用 - (void)observeValueForKeyPath:ofObject:change:context:方法
    NSValue *valueContext = [NSValue valueWithPointer:context];
    __block NSObject *trueObserver;

    dispatch_sync(self.queue, ^{
        trueObserver = [self.trampolines objectForKey:valueContext];
    });

    if (trueObserver != nil) {
        [trueObserver observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

你可能感兴趣的:(ReactiveObjC 源码阅读笔记 (RACObserve()))