RxSwift文檔補充二(Delegate)

DelegateProxyType

View只能注冊一個delegate/datasource觉阅,而DelegateProxyType協(xié)議允許使用正常的delegates和Rx觀察序列擂仍。

Proxies為特定的視圖存儲有關observers沫换、subscriptions和delegates的信息鼎天。

DelegateProxyType的實現(xiàn)不能直接初始化,應該使用獲取實現(xiàn)DelegateProxyType水慨、proxy初始化實例的方法蔚叨。

這或多或少就是DelegateProxyType的工作原理:

      +-------------------------------------------+
      |                                           |                           
      | UIView subclass (UIScrollView)            |                           
      |                                           |
      +-----------+-------------------------------+                           
                  |                                                           
                  | Delegate                                                  
                  |                                                           
                  |                                                           
      +-----------v-------------------------------+                           
      |                                           |                           
      | Delegate proxy : DelegateProxyType        +-----+---->  Observable<T1>
      |                , UIScrollViewDelegate     |     |
      +-----------+-------------------------------+     +---->  Observable<T2>
                  |                                     |                     
                  |                                     +---->  Observable<T3>
                  |                                     |                     
                  | forwards events                     |
                  | to custom delegate                  |
                  |                                     v                     
      +-----------v-------------------------------+                           
      |                                           |                           
      | Custom delegate (UIScrollViewDelegate)    |                           
      |                                           |
      +-------------------------------------------+                           

因為RxCocoa需要自動創(chuàng)建那些代理,并且擁有delegates的View可以是繼承的UITableView: UIScrollView: UIView

相應的代理也是繼承的UITableViewDelegate: UIScrollViewDelegate: NSObject

這個機制可以通過在使用rx.*之前(例如appDidFinishLaunching)粥诫,在registerKnownImplementations或程序的其他一些地方使用執(zhí)行下面的代碼片段來擴展油航。

    RxScrollViewDelegateProxy.register { RxTableViewDelegateProxy(parentObject: $0) }

協(xié)議分析

public protocol DelegateProxyType: class {
    /// 關聯(lián)類型,父對象->持有代理對象的類
    associatedtype ParentObject: AnyObject
    /// 關聯(lián)類型怀浆,代理->上面ParentObject的代理類
    associatedtype Delegate
    
    /// 這里需要枚舉調(diào)用擴展的DelegateProxy子類的`register`
    static func registerKnownImplementations()

    /// 代理的唯一id
    static var identifier: UnsafeRawPointer { get }

    /// 返回對象特定的代理屬性
    ///
    /// 對象可以有多個代理屬性
    ///
    /// 每個代理屬性需要在自己的類中實現(xiàn)`DelegateProxyType`
    ///
    /// 這是抽象方法
    ///
    /// - parameter object: 擁有代理屬性的對象
    /// - returns: 代理屬性的值
    static func currentDelegate(for object: ParentObject) -> Delegate?

    /// 為對象設置特定的代理屬性
    ///
    /// 對象可以有多個代理屬性
    ///
    /// 每個代理屬性需要在自己的類中實現(xiàn)`DelegateProxyType`
    ///
    /// 這是抽象方法
    ///
    /// - parameter toObject: 擁有代理屬性的對象
    /// - parameter delegate: 代理屬性的值
    static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject)

    /// 返回接收所有通過`self`轉(zhuǎn)發(fā)的消息的正常代理的引用
    ///
    /// - returns: 引用的值或者為空
    func forwardToDelegate() -> Delegate?

    /// 設置接收所有通過`self`轉(zhuǎn)發(fā)的消息的正常代理的引用
    ///
    /// - parameter forwardToDelegate: 接收所有通過`self`轉(zhuǎn)發(fā)的消息的代理的引用
    /// - parameter retainDelegate: `self`是否強引用`forwardToDelegate`
    func setForwardToDelegate(_ forwardToDelegate: Delegate?, retainDelegate: Bool)
}

DelegateProxy

DelegateProxy作為實現(xiàn)DelegateProxyType協(xié)議的基類谊囚,它的實現(xiàn)不是線程安全的,只能在主線程中使用执赡。

_RXDelegateProxy

_RXDelegateProxyDelegateProxy的父類镰踏,由Objective-C實現(xiàn),繼承自NSObjective沙合,.h文件如下:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface _RXDelegateProxy : NSObject

@property (nonatomic, weak, readonly) id _forwardToDelegate;

-(void)_setForwardToDelegate:(id __nullable)forwardToDelegate retainDelegate:(BOOL)retainDelegate NS_SWIFT_NAME(_setForwardToDelegate(_:retainDelegate:)) ;

-(BOOL)hasWiredImplementationForSelector:(SEL)selector;
-(BOOL)voidDelegateMethodsContain:(SEL)selector;

-(void)_sentMessage:(SEL)selector withArguments:(NSArray*)arguments;
-(void)_methodInvoked:(SEL)selector withArguments:(NSArray*)arguments;

@end

首先分析幾個相關的方法函數(shù)

RX_is_method_with_description_void函數(shù):

BOOL RX_is_method_with_description_void(struct objc_method_description method) {
    return strncmp(method.types, @encode(void), 1) == 0;
}

代碼分析:

  • 這個函數(shù)的作用是通過一個方法描述結構體objc_method_description判斷此方法是否有返回值
  • 使用strncmp函數(shù)比較方法參數(shù)編碼字符串和void的類型編碼的第一個字符是否相等

collectVoidSelectorsForProtocol:方法:

+(NSSet*)collectVoidSelectorsForProtocol:(Protocol *)protocol {
    NSMutableSet *selectors = [NSMutableSet set];

    unsigned int protocolMethodCount = 0;
    struct objc_method_description *pMethods = protocol_copyMethodDescriptionList(protocol, NO, YES, &protocolMethodCount);

    for (unsigned int i = 0; i < protocolMethodCount; ++i) {
        struct objc_method_description method = pMethods[i];
        if (RX_is_method_with_description_void(method)) {
            [selectors addObject:SEL_VALUE(method.name)];
        }
    }
            
    free(pMethods);

    unsigned int numberOfBaseProtocols = 0;
    Protocol * __unsafe_unretained * pSubprotocols = protocol_copyProtocolList(protocol, &numberOfBaseProtocols);

    for (unsigned int i = 0; i < numberOfBaseProtocols; ++i) {
        [selectors unionSet:[self collectVoidSelectorsForProtocol:pSubprotocols[i]]];
    }
    
    free(pSubprotocols);

    return selectors;
}

代碼分析:

  • 此方法的作用是返回一個協(xié)議及其繼承的協(xié)議中定義的所有沒有返回值的方法的集合
  • 獲取該協(xié)議上定義的所有方法奠伪,將沒有返回值的方法的selector轉(zhuǎn)換為一個NSValue值加入到集合中
  • 獲取該協(xié)議繼承的所有協(xié)議,合并通過遞歸的方式獲取協(xié)議上定義的所有沒有返回值的方法的集合灌诅,然后返回

主要方法分析

initialize方法是在這個類接收第一條消息之前調(diào)用:

+(void)initialize {
    @synchronized (_RXDelegateProxy.class) {
        if (voidSelectorsPerClass == nil) {
            voidSelectorsPerClass = [[NSMutableDictionary alloc] init];
        }

        NSMutableSet *voidSelectors = [NSMutableSet set];

#define CLASS_HIERARCHY_MAX_DEPTH 100

        NSInteger  classHierarchyDepth = 0;
        Class      targetClass         = NULL;

        for (classHierarchyDepth = 0, targetClass = self;
             classHierarchyDepth < CLASS_HIERARCHY_MAX_DEPTH && targetClass != nil;
             ++classHierarchyDepth, targetClass = class_getSuperclass(targetClass)
        ) {
            unsigned int count;
            Protocol *__unsafe_unretained *pProtocols = class_copyProtocolList(targetClass, &count);
            
            for (unsigned int i = 0; i < count; i++) {
                NSSet *selectorsForProtocol = [self collectVoidSelectorsForProtocol:pProtocols[i]];
                [voidSelectors unionSet:selectorsForProtocol];
            }
            
            free(pProtocols);
        }

        if (classHierarchyDepth == CLASS_HIERARCHY_MAX_DEPTH) {
            NSLog(@"Detected weird class hierarchy with depth over %d. Starting with this class -> %@", CLASS_HIERARCHY_MAX_DEPTH, self);
#if DEBUG
            abort();
#endif
        }
        
        voidSelectorsPerClass[CLASS_VALUE(self)] = voidSelectors;
    }
}

代碼分析:

  • 可變字典類型的靜態(tài)變量voidSelectorsPerClass在沒有值時初始化
  • 遍歷該類的繼承系統(tǒng)芳来,然后遍歷每個類遵守的協(xié)議,使用上面分析的collectVoidSelectorsForProtocol:方法拿到協(xié)議定義的沒有返回值的方法集合然后合并到一起
  • 最后將該類轉(zhuǎn)化為NSValue值做為key猜拾,將上面得到的集合作為值存儲到voidSelectorsPerClass字典中

_forwardToDelegate取值方法:

-(id)_forwardToDelegate {
    return __forwardToDelegate;
}

代碼分析:

  • 只讀屬性_forwardToDelegate的getter方法即舌,直接返回實例變量__forwardToDelegate的值

_setForwardToDelegate:retainDelegate:方法:

-(void)_setForwardToDelegate:(id __nullable)forwardToDelegate retainDelegate:(BOOL)retainDelegate {
    __forwardToDelegate = forwardToDelegate;
    if (retainDelegate) {
        self.strongForwardDelegate = forwardToDelegate;
    }
    else {
        self.strongForwardDelegate = nil;
    }
}

代碼分析:

  • forwardToDelegate賦值給實例變量__forwardToDelegate
  • 根據(jù)retainDelegate決定是否把forwardToDelegate賦值給strongForwardDelegate強引用屬性

非常重要的一個方法forwardInvocation:

-(void)forwardInvocation:(NSInvocation *)anInvocation {
    BOOL isVoid = RX_is_method_signature_void(anInvocation.methodSignature);
    NSArray *arguments = nil;
    if (isVoid) {
        arguments = RX_extract_arguments(anInvocation);
        [self _sentMessage:anInvocation.selector withArguments:arguments];
    }
    
    if (self._forwardToDelegate && [self._forwardToDelegate respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:self._forwardToDelegate];
    }

    if (isVoid) {
        [self _methodInvoked:anInvocation.selector withArguments:arguments];
    }
}

代碼分析:

  • 該方法是在接受到一個未知消息進行消息轉(zhuǎn)發(fā)時調(diào)用的,可以移步我以前的一篇博客了解消息轉(zhuǎn)發(fā)
  • 首先判斷未知消息是否有返回值
  • 如果該未知消息沒有返回值就獲取消息的參數(shù)列表并執(zhí)行_sentMessage:withArguments:方法
  • 如果_forwardToDelegate屬性能夠響應該消息挎袜,將該未知消息轉(zhuǎn)發(fā)給_forwardToDelegate屬性對象
  • 最后如果該未知消息沒有返回值就執(zhí)行_methodInvoked:withArguments:方法

DelegateProxyFactory

遵守DelegateProxyType協(xié)議的代理對象的工廠類顽聂,主要作用是存儲生成遵守DelegateProxyType協(xié)議的代理對象的閉包。

初始化:

        private var _factories: [ObjectIdentifier: ((AnyObject) -> AnyObject)]
        private var _delegateProxyType: Any.Type
        private var _identifier: UnsafeRawPointer

        private init<DelegateProxy: DelegateProxyType>(for proxyType: DelegateProxy.Type) {
            self._factories = [:]
            self._delegateProxyType = proxyType
            self._identifier = proxyType.identifier
        }

代碼分析:

  • _factories屬性使用空字典初始化
  • _delegateProxyType屬性使用遵守DelegateProxyType協(xié)議的代理類初始化
  • _identifier屬性使用遵守DelegateProxyType協(xié)議的代理類的id初始化

extend函數(shù):

        fileprivate func extend<DelegateProxy: DelegateProxyType, ParentObject>(make: @escaping (ParentObject) -> DelegateProxy) {
                MainScheduler.ensureRunningOnMainThread()
                precondition(self._identifier == DelegateProxy.identifier, "Delegate proxy has inconsistent identifier")
                guard self._factories[ObjectIdentifier(ParentObject.self)] == nil else {
                    rxFatalError("The factory of \(ParentObject.self) is duplicated. DelegateProxy is not allowed of duplicated base object type.")
                }
                self._factories[ObjectIdentifier(ParentObject.self)] = { make(castOrFatalError($0)) }
        }

代碼分析:

  • 首先保證在主線程
  • 保證_identifier屬性的值與泛型DelegateProxyidentifier類屬性相等
  • 保證_factories屬性字典中沒有存儲以泛型ParentObject相應的ObjectIdentifier作為key的值
  • 以泛型ParentObject相應的ObjectIdentifier作為key盯仪,將生成Rx代理對象的閉包存在_factories屬性中

createProxy函數(shù):

fileprivate func createProxy(for object: AnyObject) -> AnyObject {
            MainScheduler.ensureRunningOnMainThread()
            var maybeMirror: Mirror? = Mirror(reflecting: object)
            while let mirror = maybeMirror {
                if let factory = self._factories[ObjectIdentifier(mirror.subjectType)] {
                    return factory(object)
                }
                maybeMirror = mirror.superclassMirror
            }
            rxFatalError("DelegateProxy has no factory of \(object). Implement DelegateProxy subclass for \(object) first.")
        }

代碼分析:

  • 首先保證在主線程
  • 遍歷參數(shù)對象的繼承系統(tǒng)紊搪,用類相應的ObjectIdentifier作為key_factories屬性中獲取生成Rx代理對象的閉包,取到閉包就返回執(zhí)行閉包的結果
  • 最終取不到閉包就終止

類屬性_sharedFactories是以代理的id作為key全景、工廠對象作為值的字典耀石,用于緩存各遵守DelegateProxyType協(xié)議的代理類的工廠對象:

        private static var _sharedFactories: [UnsafeRawPointer: DelegateProxyFactory] = [:]

sharedFactory函數(shù)根據(jù)遵守DelegateProxyType協(xié)議的代理類的id獲取對應的工廠對象,代碼如下:

        fileprivate static func sharedFactory<DelegateProxy: DelegateProxyType>(for proxyType: DelegateProxy.Type) -> DelegateProxyFactory {
            MainScheduler.ensureRunningOnMainThread()
            let identifier = DelegateProxy.identifier
            if let factory = _sharedFactories[identifier] {
                return factory
            }
            let factory = DelegateProxyFactory(for: proxyType)
            _sharedFactories[identifier] = factory
            DelegateProxy.registerKnownImplementations()
            return factory
        }

代碼分析:

  • 保證在主線程
  • 根據(jù)遵守DelegateProxyType協(xié)議的代理類id從_sharedFactories字典中獲取工廠對象爸黄,取到就返回
  • _sharedFactories字典中取不到工廠對象滞伟,就創(chuàng)建一個并存到字典中
  • 執(zhí)行遵守DelegateProxyType協(xié)議的代理類的registerKnownImplementations函數(shù)并返回工廠對象

MessageDispatcher

MessageDispatcher的本質(zhì)是一個PublishSubject揭鳞,把協(xié)議中的每個函數(shù)對應一個MessageDispatcher,進而將函數(shù)的調(diào)用轉(zhuǎn)化為Observable序列梆奈,其實現(xiàn)如下:

    private final class MessageDispatcher {
        private let dispatcher: PublishSubject<[Any]>
        private let result: Observable<[Any]>

        fileprivate let selector: Selector

        init<P, D>(selector: Selector, delegateProxy _delegateProxy: DelegateProxy<P, D>) {
            weak var weakDelegateProxy = _delegateProxy

            let dispatcher = PublishSubject<[Any]>()
            self.dispatcher = dispatcher
            self.selector = selector

            self.result = dispatcher
                .do(onSubscribed: { weakDelegateProxy?.checkSelectorIsObservable(selector); weakDelegateProxy?.reset() }, onDispose: { weakDelegateProxy?.reset() })
                .share()
                .subscribeOn(mainScheduler)
        }

        var on: (Event<[Any]>) -> Void {
            return self.dispatcher.on
        }

        var hasObservers: Bool {
            return self.dispatcher.hasObservers
        }

        func asObservable() -> Observable<[Any]> {
            return self.result
        }
    }

代碼分析:

  • 初始化時創(chuàng)建一個PublishSubject存儲在dispatcher屬性中
  • 初始化時將dispatcher屬性中存儲的PublishSubject執(zhí)行do操作符野崇,并在PublishSubject被訂閱后執(zhí)行代理對象的checkSelectorIsObservable函數(shù)檢查是否能將selector轉(zhuǎn)化為Observable序列,然后執(zhí)行代理對象的reset函數(shù)重新設置代理亩钟。在PublishSubject銷毀時執(zhí)行代理對象的reset函數(shù)重新設置代理乓梨。然后執(zhí)行share操作共享元素,接著執(zhí)行subscribeOn(mainScheduler)操作保證序列在主線程中執(zhí)行各種操作清酥,最后將最終得到的Observable序列存儲到result屬性中
  • on函數(shù)直接調(diào)用dispatcher屬性中存儲的PublishSubjecton操作
  • asObservable函數(shù)直接返回初始化時構造好的result屬性

DelegateProxy實現(xiàn)

首先分析一下DelegateProxy的屬性:

  • _sentMessageForSelector屬性是一個以SelectorkeyMessageDispatcher為值的字典
  • _methodInvokedForSelector屬性也是一個以SelectorkeyMessageDispatcher為值的字典扶镀,與_sentMessageForSelector相比主要是他們存儲的MessageDispatcher使用的時機不同(后面再講)
  • _parentObject屬性存儲持有代理對象的對象
  • _currentDelegateFor屬性是一個閉包,用來獲取_parentObject屬性的代理對象
  • _setCurrentDelegateTo屬性也是一個閉包焰轻,用來設置_parentObject屬性的代理對象

sentMessage函數(shù)分析:

        open func sentMessage(_ selector: Selector) -> Observable<[Any]> {
            MainScheduler.ensureRunningOnMainThread()

            let subject = self._sentMessageForSelector[selector]

            if let subject = subject {
                return subject.asObservable()
            }
            else {
                let subject = MessageDispatcher(selector: selector, delegateProxy: self)
                self._sentMessageForSelector[selector] = subject
                return subject.asObservable()
            }
        }

代碼分析:

  • 使用selector參數(shù)從_sentMessageForSelector屬性中取MessageDispatcher對象狈惫,
  • 取到值就轉(zhuǎn)化為Observable序列返回
  • 沒有取到值就創(chuàng)建一個MessageDispatcher對象,存儲到_sentMessageForSelector屬性中鹦马,然后轉(zhuǎn)化為Observable序列返回

methodInvoked函數(shù)的實現(xiàn)與上面分析的函數(shù)相同。

checkSelectorIsObservable函數(shù)分析:

  • 該函數(shù)的作用是檢查selector參數(shù)對應的函數(shù)是否能夠轉(zhuǎn)化為Observable序列
  • 首先檢查自己沒有實現(xiàn)selector參數(shù)對應的函數(shù)
  • 接著檢查自己遵守的協(xié)議定義的沒有返回值的函數(shù)集合包含有selector參數(shù)對應的函數(shù)
  • 最后檢查_forwardToDelegate屬性不能響應selector參數(shù)對應的函數(shù)

_sentMessage_methodInvoked函數(shù)分析:

        open override func _sentMessage(_ selector: Selector, withArguments arguments: [Any]) {
            self._sentMessageForSelector[selector]?.on(.next(arguments))
        }

        open override func _methodInvoked(_ selector: Selector, withArguments arguments: [Any]) {
            self._methodInvokedForSelector[selector]?.on(.next(arguments))
        }

代碼分析:

  • _sentMessage_methodInvoked函數(shù)都只是取出對應的MessageDispatcher對象忆肾,然后執(zhí)行on操作來發(fā)射元素
  • 此時應該聯(lián)系DelegateProxy的父類_RXDelegateProxyforwardInvocation:方法來分析
  • 在收到?jīng)]有返回值的未知消息時荸频,會先后執(zhí)行這兩個方法(注意它們的執(zhí)行順序)

DelegateProxyType擴展重要函數(shù)實現(xiàn)

register函數(shù)就是把生成對象閉包存儲到DelegateProxyFactory中:

    private static var factory: DelegateProxyFactory {
        return DelegateProxyFactory.sharedFactory(for: self)
    }
    public static func register<Parent>(make: @escaping (Parent) -> Self) {
        self.factory.extend(make: make)
    }

代碼分析:

  • DelegateProxyFactory.sharedFactory首次調(diào)用時內(nèi)部會調(diào)用registerKnownImplementations函數(shù)
  • 前面說過需要在實現(xiàn)registerKnownImplementations函數(shù)時調(diào)用實現(xiàn)的代理類的register函數(shù)注冊一個代理對象生成的閉包
  • DelegateProxyFactoryextend函數(shù)就是用來存儲代理對象生成閉包的

proxy函數(shù):

    public static func createProxy(for object: AnyObject) -> Self {
        return castOrFatalError(factory.createProxy(for: object))
    }
    public static func proxy(for object: ParentObject) -> Self {
        MainScheduler.ensureRunningOnMainThread()

        let maybeProxy = self.assignedProxy(for: object)

        let proxy: AnyObject
        if let existingProxy = maybeProxy {
            proxy = existingProxy
        }
        else {
            proxy = castOrFatalError(self.createProxy(for: object))
            self.assignProxy(proxy, toObject: object)
            assert(self.assignedProxy(for: object) === proxy)
        }
        let currentDelegate = self._currentDelegate(for: object)
        let delegateProxy: Self = castOrFatalError(proxy)

        if currentDelegate !== delegateProxy {
            delegateProxy._setForwardToDelegate(currentDelegate, retainDelegate: false)
            assert(delegateProxy._forwardToDelegate() === currentDelegate)
            self._setCurrentDelegate(proxy, to: object)
            assert(self._currentDelegate(for: object) === proxy)
            assert(delegateProxy._forwardToDelegate() === currentDelegate)
        }

        return delegateProxy
    }

代碼分析:

  • 首先使用runtime技術中的assignedProxy函數(shù)獲取關聯(lián)屬性,其實就是緩存的代理對象
  • 如果沒有值就執(zhí)行createProxy函數(shù)客冈,其實就是從DelegateProxyFactory中取出注冊的生成Rx代理對象的閉包執(zhí)行旭从,進而生成Rx代理對象
  • 獲取參數(shù)ParentObject的代理對象
  • 如果代理對象和Rx代理對象不是同一個對象,那么將代理對象賦值給Rx代理對象的forwardToDelegate屬性场仲,將Rx代理對象作為參數(shù)ParentObject真正的代理對象
  • 返回Rx代理對象

總結

羅里吧嗦的分析這么久的代碼和悦,感覺調(diào)理不是很清楚,最后總結下渠缕,Rx中代理的實現(xiàn)過程鸽素,就以UIScrollViewDelegate協(xié)議的Rx實現(xiàn)為例。

首先構建類RxScrollViewDelegateProxy繼承DelegateProxy基類亦鳞,同時遵守DelegateProxyType馍忽、UIScrollViewDelegate協(xié)議協(xié)議。在RxScrollViewDelegateProxy中實現(xiàn)registerKnownImplementations函數(shù)燕差,為遵守UIScrollViewDelegate協(xié)議的對象注冊一個Rx代理對象生成的閉包:

open class RxScrollViewDelegateProxy
    : DelegateProxy<UIScrollView, UIScrollViewDelegate>
    , DelegateProxyType 
    , UIScrollViewDelegate {
    public init(scrollView: ParentObject) {
        self.scrollView = scrollView
        super.init(parentObject: scrollView, delegateProxy: RxScrollViewDelegateProxy.self)
    }
    public static func registerKnownImplementations() {
        self.register { RxScrollViewDelegateProxy(scrollView: $0) }
        self.register { RxTableViewDelegateProxy(tableView: $0) }
        self.register { RxCollectionViewDelegateProxy(collectionView: $0) }
        self.register { RxTextViewDelegateProxy(textView: $0) }
    }
}

說明:此處的registerKnownImplementations函數(shù)把UIScrollViewDelegate協(xié)議的子協(xié)議的Rx代理對象的生成閉包一并注冊了遭笋。

DelegateProxyType協(xié)議定義的下面只讀屬性在DelegateProxyType擴展中已經(jīng)實現(xiàn),所以不需要不實現(xiàn):

extension DelegateProxyType { UnsafeRawPointer {
        let delegateIdentifier = ObjectIdentifier(Delegate.self)
        let integerIdentifier = Int(bitPattern: delegateIdentifier)
        return UnsafeRawPointer(bitPattern: integerIdentifier)!
    }
}

DelegateProxyType協(xié)議定義的下面兩個函數(shù)在DelegateProxyType擴展中已經(jīng)實現(xiàn):

extension DelegateProxyType where ParentObject: HasDelegate, Self.Delegate == ParentObject.Delegate {
    public static func currentDelegate(for object: ParentObject) -> Delegate? {
        return object.delegate
    }

    public static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject) {
        object.delegate = delegate
    }
}

上面DelegateProxyType協(xié)議定義的兩個函數(shù)雖然在DelegateProxyType擴展中已經(jīng)實現(xiàn)但是需要滿足兩個條件:

  • 持有Rx代理對象的類必須遵守HasDelegate協(xié)議
  • Self.Delegate的類型和持有Rx代理對象的類的Delegate類型必須相同

所以擴展UIScrollView類徒探,讓其準守HasDelegate協(xié)議并且讓Delegate類型和Rx代理對象的類的Delegate類型必須相同:

extension UIScrollView: HasDelegate {
    public typealias Delegate = UIScrollViewDelegate
}

DelegateProxyType協(xié)議定義的下面兩個函數(shù)在Rx代理基類DelegateProxy中已經(jīng)實現(xiàn)瓦呼,也不需要再實現(xiàn)了:

        open func forwardToDelegate() -> Delegate? {
            return castOptionalOrFatalError(self._forwardToDelegate)
        }
        open func setForwardToDelegate(_ delegate: Delegate?, retainDelegate: Bool) {
            #if DEBUG // 4.0 all configurations
                MainScheduler.ensureRunningOnMainThread()
            #endif
            self._setForwardToDelegate(delegate, retainDelegate: retainDelegate)

            let sentSelectors: [Selector] = self._sentMessageForSelector.values.filter { $0.hasObservers }.map { $0.selector }
            let invokedSelectors: [Selector] = self._methodInvokedForSelector.values.filter { $0.hasObservers }.map { $0.selector }
            let allUsedSelectors = sentSelectors + invokedSelectors

            for selector in Set(allUsedSelectors) {
                self.checkSelectorIsObservable(selector)
            }

            self.reset()
        }

協(xié)議函數(shù)的Observeble序列實現(xiàn)

有兩種方法可以將協(xié)議中的函數(shù)實現(xiàn)為Observeble序列

Subject方式

這種方式需要在RxScrollViewDelegateProxy類中,定義對應的UIScrollViewDelegate協(xié)議中函數(shù)對應的Subject测暗,并且實現(xiàn)UIScrollViewDelegate協(xié)議的函數(shù)央串,在協(xié)議的函數(shù)中調(diào)用Subjecton操作來發(fā)射元素:

    private var _contentOffsetBehaviorSubject: BehaviorSubject<CGPoint>?
    private var _contentOffsetPublishSubject: PublishSubject<()>?
    
    internal var contentOffsetBehaviorSubject: BehaviorSubject<CGPoint> {
        if let subject = _contentOffsetBehaviorSubject {
            return subject
        }

        let subject = BehaviorSubject<CGPoint>(value: self.scrollView?.contentOffset ?? CGPoint.zero)
        _contentOffsetBehaviorSubject = subject

        return subject
    }
    internal var contentOffsetPublishSubject: PublishSubject<()> {
        if let subject = _contentOffsetPublishSubject {
            return subject
        }

        let subject = PublishSubject<()>()
        _contentOffsetPublishSubject = subject

        return subject
    }
    
    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if let subject = _contentOffsetBehaviorSubject {
            subject.on(.next(scrollView.contentOffset))
        }
        if let subject = _contentOffsetPublishSubject {
            subject.on(.next(()))
        }
        self._forwardToDelegate?.scrollViewDidScroll?(scrollView)
    }
    
    deinit {
        if let subject = _contentOffsetBehaviorSubject {
            subject.on(.completed)
        }

        if let subject = _contentOffsetPublishSubject {
            subject.on(.completed)
        }
    }

說明:開頭就說了DelegateProxyType協(xié)議允許使用正常的delegates和Rx觀察序列磨澡,因為RxScrollViewDelegateProxy.proxy(for: base)中的proxy操作會將正常的代理對象存儲到Rx代理對象的forwardToDelegate屬性中,所以在協(xié)議函數(shù)的實現(xiàn)中調(diào)一下forwardToDelegate對象的協(xié)議函數(shù)蹋辅,達到對正常代理對象的支持(可以參看上面相應的代碼分析)钱贯。

使用RxScrollViewDelegateProxy.proxy(for: base)提供一個Rx代理對象,為了方便一般都是封裝到一個BaseUIScrollView類的Reactive擴展中侦另,最后拿到相應的Subject將其轉(zhuǎn)化為Observeble序列即可:

public var didScroll: ControlEvent<Void> {
            let source = RxScrollViewDelegateProxy.proxy(for: base).contentOffsetPublishSubject
            return ControlEvent(events: source)
        }

消息轉(zhuǎn)發(fā)方式

消息轉(zhuǎn)發(fā)方式不需要實現(xiàn)UIScrollViewDelegate協(xié)議中函數(shù)秩命,因為沒有實現(xiàn),所以會走基類_RXDelegateProxy的消息轉(zhuǎn)發(fā)方法forwardInvocation:(可參看前面相應的代碼分析)褒傅,主要做了如下三件事:

  • 執(zhí)行_sentMessage:withArguments:方法
  • 將未知消息轉(zhuǎn)發(fā)給_forwardToDelegate屬性對象
  • 執(zhí)行_methodInvoked:withArguments:方法

_sentMessage:withArguments:_methodInvoked:withArguments:方法在基類DelegateProxy中都已實現(xiàn)(可以參看上面相應的代碼分析)弃锐,主要就是根據(jù)selector獲取相應的MessageDispatcher(可以參看上面相應的代碼分析)對象執(zhí)行on操作發(fā)射元素。將未知消息轉(zhuǎn)發(fā)給_forwardToDelegate屬性對象是為了對正常代理對象的支持殿托。

基類_RXDelegateProxy暴露sentMessagemethodInvoked函數(shù)(可以參看上面相應的代碼分析)根據(jù)selector構建相應的MessageDispatcher對象霹菊,并轉(zhuǎn)化為Observeble序列。

綜上所述支竹,所以直接執(zhí)行Rx代理對象的sentMessagemethodInvoked函數(shù)就可以獲得相應的Observeble序列:

    extension Reactive where Base: UIScrollView {
        public var delegate: DelegateProxy<UIScrollView, UIScrollViewDelegate> {
            return RxScrollViewDelegateProxy.proxy(for: base)
        }
        public var willBeginDecelerating: ControlEvent<Void> {
            let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillBeginDecelerating(_:))).map { _ in }
            return ControlEvent(events: source)
        }
    }
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末旋廷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子礼搁,更是在濱河造成了極大的恐慌饶碘,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件馒吴,死亡現(xiàn)場離奇詭異扎运,居然都是意外死亡,警方通過查閱死者的電腦和手機饮戳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門豪治,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扯罐,你說我怎么就攤上這事负拟。” “怎么了篮赢?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵齿椅,是天一觀的道長。 經(jīng)常有香客問我启泣,道長涣脚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任寥茫,我火速辦了婚禮遣蚀,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己芭梯,他們只是感情好险耀,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著玖喘,像睡著了一般甩牺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音碎紊,去河邊找鬼。 笑死搞乏,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的戒努。 我是一名探鬼主播请敦,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼储玫!你這毒婦竟也來了侍筛?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤撒穷,失蹤者是張志新(化名)和其女友劉穎勾笆,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體桥滨,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年弛车,在試婚紗的時候發(fā)現(xiàn)自己被綠了齐媒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡纷跛,死狀恐怖喻括,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贫奠,我是刑警寧澤唬血,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站唤崭,受9級特大地震影響拷恨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谢肾,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一腕侄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦冕杠、人聲如沸微姊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兢交。三九已至,卻和暖如春笼痹,著一層夾襖步出監(jiān)牢的瞬間配喳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工与倡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留界逛,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓纺座,卻偏偏與公主長得像息拜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子净响,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354