RAC 中的 RACObserve 監(jiān)聽流程分析

我們都知道 ReactiveObjC 的編程思想主要是 響應(yīng)式編程 的概念 , 而 RACObserve 是使用了原生的 KVO響應(yīng)式編程 相結(jié)合的產(chǎn)物。

現(xiàn)在開始對(duì) RACObserve 的監(jiān)聽流程進(jìn)行分析,現(xiàn)在VC聲明一下屬性,如下:

//ViewController.m
@property (nonatomic, strong) Dog *dog;
@property (nonatomic, copy) NSString *name;

我們使用的 RACObserve 是一個(gè)宏定義,里面?zhèn)魅氲膮?shù)為:

RACObserve(TARGET, KEYPATH)

[RACObserve(self, name) subscribeNext:^(id  _Nullable x) {      
    // 這里會(huì)得到被監(jiān)聽值的最終結(jié)果
  }];

那么這個(gè)宏定義 RACObserve 實(shí)際又是一個(gè)什么東西呢?我們看一下源碼:


#define _RACObserve(TARGET, KEYPATH) \
({ \
//   把傳入的被觀察對(duì)象進(jìn)行了 weak 引用
    __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

RACObserve 實(shí)質(zhì)是繼續(xù)調(diào)用了 _RACObserve(TARGET, KEYPATH)

#define _RACObserve(TARGET, KEYPATH) \
({ \
    __weak id target_ = (TARGET); \
    [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \
})
// 是  NSObject+RACPropertySubscribing.h 里的兩個(gè)方法
[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self];

NSObject+RACPropertySubscribing

看第一個(gè)方法即可 OS_OBJECT_HAVE_OBJC_SUPPORT

//  NSObject+RACPropertySubscribing.h
#if OS_OBJECT_HAVE_OBJC_SUPPORT
- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath 
                           observer:(__weak NSObject *)observer;
#else
// Swift builds with OS_OBJECT_HAVE_OBJC_SUPPORT=0 for Playgrounds and LLDB :(
- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath 
                           observer:(NSObject *)observer;
#endif

這里實(shí)際是封裝了一個(gè) RACSignal 對(duì)象,外部調(diào)用的 RACObserve(TARGET, KEYPATH) 是返回一個(gè)(RACSignal *)對(duì)象

// NSObject+RACPropertySubscribing.m
- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath 
                          observer:(__weak NSObject *)observer {
/*
  這里實(shí)際是封裝了一個(gè) RACSignal 對(duì)象, 
  RACObserve() 的宏返回的是 (RACSignal *)
*/
    return [[[self  rac_valuesAndChangesForKeyPath:keyPath                                   options:NSKeyValueObservingOptionInitial
                    observer:observer]
        map:^(RACTuple *value) {
            return value[0];
        }]
setNameWithFormat:@"RACObserve(%@, %@)", RACDescription(self), keyPath];
}


- (RACSignal *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath 
                      options:(NSKeyValueObservingOptions)options 
                      observer:(__weak NSObject *)weakObserver {

    NSObject *strongObserver = weakObserver;
    keyPath = [keyPath copy];

// 產(chǎn)生了一個(gè)遞歸鎖
    NSRecursiveLock *objectLock = [[NSRecursiveLock alloc] init];
    objectLock.name = @"org.reactivecocoa.ReactiveObjC.NSObjectRACPropertySubscribing";

    __weak NSObject *weakSelf = self;

// 創(chuàng)建了一個(gè) 銷毀信號(hào)量, 假如:vc 調(diào)用了 RACObserve(TARGET, KEYPATH),但vc被銷毀了,這個(gè)信號(hào)就會(huì)發(fā)出一個(gè)銷毀信號(hào)
    RACSignal *deallocSignal = [[RACSignal
        zip:@[
            self.rac_willDeallocSignal,
            strongObserver.rac_willDeallocSignal ?: [RACSignal never]
        ]]
        doCompleted:^{
            [objectLock lock];
            @onExit {
                [objectLock unlock];
            };
        }];

    //  takeUntil:deallocSignal  判斷當(dāng)前觀察者是否銷毀了,如果銷毀了就不會(huì)對(duì)進(jìn)入以下監(jiān)聽工作
    return [[[RACSignal
        createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
            [objectLock lock];
            @onExit {
                [objectLock unlock];
            };

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

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

            if (self == nil) {
                [subscriber sendCompleted];
                return nil;
            }

            //以下方法進(jìn)行傳值監(jiān)聽, 看這里?。?! 看這里?。。?看這里?。。?            return [self rac_observeKeyPath:keyPath options:options 
                          observer:observer
                          block:^(id value, NSDictionary *change, 
                 BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
                /*
                    將得到的新值通過block回調(diào)到這里,
                    并使用 sendNext 發(fā)送出去外部訂閱的 subscribeNext
                 */
                [subscriber sendNext:RACTuplePack(value, change)];
            }];
        }]
        takeUntil:deallocSignal]
        setNameWithFormat:@"%@ -rac_valueAndChangesForKeyPath: %@ options: %lu observer: %@", 
RACDescription(self), keyPath, (unsigned long)options, RACDescription(strongObserver)];
}

// 點(diǎn)擊此方法,跳轉(zhuǎn)到 NSObject+RACKVOWrappe.m
[self rac_observeKeyPath:keyPath options:options observer:observer 
 block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
                [subscriber sendNext:RACTuplePack(value, change)];
            }];

NSObject+RACKVOWrapper

// NSObject+RACKVOWrapper.m
- (RACDisposable *)rac_observeKeyPath:(NSString *)keyPath 
                    options:(NSKeyValueObservingOptions)options 
                    observer:(__weak NSObject *)weakObserver 
                    block:(void (^)(id, NSDictionary *, BOOL, BOOL))block

此方法上半部分做了一大堆關(guān)于 Disposable 銷毀信號(hào)準(zhǔn)備工作 ,下半部分做了銷毀操作等等,這里就不作過多的分析了;

    /* 
          獲取keyPath 的路由,
          比如 VC 有一個(gè) Dog 類并監(jiān)聽 dog.name, RACObserve(vc,dog.name)
    */
    NSArray *keyPathComponents = keyPath.rac_keyPathComponents;
  // 獲取 被監(jiān)聽對(duì)象的 "屬性列表"
    objc_property_t property = class_getProperty(object_getClass(self), keyPathHead.UTF8String);

我們重點(diǎn)看 傳值監(jiān)聽 過程,以下代碼:

/*
  KVO: 面向?qū)ο?, 誰調(diào)用誰被監(jiān)聽
  此處把所有的相關(guān)參數(shù)傳入,如:觀察者,被觀察對(duì)象,keyPath 等,
  用 RACKVOTrampoline 進(jìn)行了封裝,此類是監(jiān)聽工作開始的起點(diǎn),block的回調(diào)得到監(jiān)聽的最終結(jié)果
*/
    RACKVOTrampoline *trampoline = 
                      [[RACKVOTrampoline alloc] initWithTarget:self 
                                              observer:strongObserver 
                                               keyPath:keyPathHead 
                                               options:trampolineOptions 
                                              block:^(id trampolineTarget, id trampolineObserver, NSDictionary *change) {   
/* 
RACKVOTrampoline.m 里的響應(yīng)監(jiān)聽方法 
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
 會(huì)把新值 Block 到這里來
  */
        if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
            [firstComponentDisposable() dispose];

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

        if (value == nil) {
            block(nil, change, NO, keyPathHasOneComponent);
            return;
        }

// 銷毀操作
        RACDisposable *oldFirstComponentDisposable = [firstComponentSerialDisposable swapInDisposable:[RACCompoundDisposable compoundDisposable]];
        [oldFirstComponentDisposable dispose];

        addDeallocObserverToPropertyValue(value);

        if (keyPathHasOneComponent) {
            block(value, change, NO, keyPathHasOneComponent);
            return;
        }

        addObserverToValue(value);
        // block  返回到 (NSObject + RACPropertySubscribing.m) 的信號(hào)量定義 block里, 并 sendNext
        block([value valueForKeyPath:keyPathTail], change, NO, keyPathHasOneComponent);
    }];

點(diǎn)擊以上 RACKVOTrampoline 的實(shí)例方法 進(jìn)入到 RACKVOTrampoline.m

RACKVOTrampoline

這里的重點(diǎn)在于:RACKVOProxy.sharedProxy 這個(gè)單例做了一個(gè)神秘操作,比如 被監(jiān)聽對(duì)象 有可能需要 觀察多個(gè)對(duì)象或其他屬性(上述代碼中,被監(jiān)聽對(duì)象是 VC ,所以被監(jiān)聽的對(duì)象會(huì)有:name,dog 等等)。
可理解為:每一個(gè)被監(jiān)聽的對(duì)象封裝成一個(gè) RACKVOTrampoline 對(duì)象,
并把這些被已封裝的 trampoline 對(duì)象全部添加到 sharedProxy 單例里面統(tǒng)一監(jiān)聽。

//RACKVOTrampoline.m
- (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;

    /*
     移交代理 --- 觀察對(duì)象
    被監(jiān)聽對(duì)象 有可能需要觀察多個(gè)對(duì)象或其他屬性(如:VC的屬性有 name,dog等等),
    可理解為:每一個(gè)被監(jiān)聽的對(duì)象封裝成一個(gè) RACKVOTrampoline 對(duì)象,
    并把這些被已封裝的 trampoline 對(duì)象全部添加到 sharedProxy 單例里面統(tǒng)一監(jiān)聽,如下一行代碼:
     */
    [RACKVOProxy.sharedProxy addObserver:self forContext:(__bridge void *)self];
    
    /*
     然而 strongTarget(VC) 只需要觀察 RACKVOProxy.sharedProxy  ,
        并把相應(yīng)的 keyPath 傳給此單例即可 ,統(tǒng)一用 shareProxy 處理,如下一行代碼:
    */
    [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 {
      // 進(jìn)行移除監(jiān)聽、信號(hào)銷毀
    [self dispose];
}

#pragma mark Observation

- (void)dispose {
    NSObject *target;
    NSObject *observer;

    @synchronized (self) {
        _block = nil;

        target = self.unsafeTarget;
        observer = self.observer;

        _unsafeTarget = nil;
        _observer = nil;
    }
    // 銷毀操作
    [target.rac_deallocDisposable removeDisposable:self];
    [observer.rac_deallocDisposable removeDisposable:self];

    // 移除監(jiān)聽
    [target removeObserver:RACKVOProxy.sharedProxy forKeyPath:self.keyPath context:(__bridge void *)self];
    [RACKVOProxy.sharedProxy removeObserver:self forContext:(__bridge void *)self];
}

- (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;
// 面向?qū)ο蟮幕麨榱?,線性安全
    @synchronized (self) {
        block = self.block;
        observer = self.observer;
        target = self.weakTarget;
    }

    if (block == nil || target == nil) return;
    /*
      數(shù)值發(fā)生改變,然后調(diào)用 block 返回到 NSObject + RACKVOWrapper.m  這個(gè)類里面
      即: RACKVOTrampoline *trampoline 實(shí)例化的 block 里面
     */
    block(target, observer, change);
}
@end

RACKVOProxy

我們?cè)倏匆幌?RACKVOProxy.sharedProxy 這個(gè)單例里面有做了些什么操作:
RACKVOProxy.m 聲明了兩個(gè)屬性,分別是: NSMapTable *trampolines用于保存 RACKVOTrampoline對(duì)象;dispatch_queue_t queue用于確保線性安全

// RACKVOProxy.m

@interface RACKVOProxy()

@property (strong, nonatomic, readonly) NSMapTable *trampolines;
@property (strong, nonatomic, readonly) dispatch_queue_t queue;

@end

@implementation RACKVOProxy

+ (instancetype)sharedProxy {
    static RACKVOProxy *proxy;
    static dispatch_once_t onceToken;

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

    return proxy;
}

- (instancetype)init {
    self = [super init];
    // 串行,線性安全:確保每次數(shù)值改變是有序的
    _queue = dispatch_queue_create("org.reactivecocoa.ReactiveObjC.RACKVOProxy", DISPATCH_QUEUE_SERIAL);
    _trampolines = [NSMapTable strongToWeakObjectsMapTable];

    return self;
}

- (void)addObserver:(__weak NSObject *)observer forContext:(void *)context {
    NSValue *valueContext = [NSValue valueWithPointer:context];
    // 把被觀察的對(duì)象 放到 trampolines(表) 里去
    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 {
    NSValue *valueContext = [NSValue valueWithPointer:context];
    __block NSObject *trueObserver;

    dispatch_sync(self.queue, ^{
        // 取出被改變值的對(duì)象
        trueObserver = [self.trampolines objectForKey:valueContext];
    });

    if (trueObserver != nil) {
        /*
         使用對(duì)應(yīng)的 RACKVOTrampoline 對(duì)象返回出去給 “RACKVOTrampoline” 這個(gè)類中的
          - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
         */
        [trueObserver observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

@end

發(fā)生回調(diào)時(shí)響應(yīng)以下數(shù)據(jù),分別是修改前、修改后的數(shù)據(jù)(為了不產(chǎn)生誤會(huì),說明一下以下兩張圖可以忽略掉其他控件的屬性,我只抽出了 dog、name 來說明)

image.png
image.png

然后會(huì)通過

 [trueObserver observeValueForKeyPath:keyPath ofObject:object change:change context:context];

發(fā)送出去 RACKVOTrampoline.m

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 

以下附上一張流程圖:


image.png

以上內(nèi)容純粹個(gè)人見解,僅用于分享交流;如有描述不當(dāng)之處,歡迎指出。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容