DelegateProxy的底層實現(xiàn)

以擴展CLLocationManager和MKMapView為例,探索DelegateProxy的底層實現(xiàn)

Extension CLLocationManager

Question:
1. 怎樣為CLLocationManager的實例添加rx名稱空間扳碍?
2. 為CLLocationManager的實例的rx名稱空間里定義可觀察存儲屬性
3. 這個可觀察存儲屬性如何獲取CLLocationManager的delegate接收到的數(shù)據(jù)并發(fā)起事件給訂閱者桑阶?
4. DelegateProxy的作用是什么欲间?
5. DelegateProxy如何代理CLLocationManager的delegate接收到的數(shù)據(jù)继阻?

1.怎樣為CLLocationManager添加rx名稱空間

// 定義在RxSwift中的Reactive.swift文件中
public struct Reactive<Base> {
    /// Base object to extend.
    public let base: Base

    /// Creates extensions with base object.
    ///
    /// - parameter base: Base object.
    public init(_ base: Base) {
        self.base = base
    }
}

public protocol ReactiveCompatible {
    /// Extended type
    associatedtype ReactiveBase

    @available(*, deprecated, renamed: "ReactiveBase")
    typealias CompatibleType = ReactiveBase

    /// Reactive extensions.
    static var rx: Reactive<ReactiveBase>.Type { get set }

    /// Reactive extensions.
    var rx: Reactive<ReactiveBase> { get set }
}

extension ReactiveCompatible {
    /// Reactive extensions.
    public static var rx: Reactive<Self>.Type {
        get {
            return Reactive<Self>.self
        }
        // swiftlint:disable:next unused_setter_value
        set {
            // this enables using Reactive to "mutate" base type
        }
    }

    /// Reactive extensions.
    public var rx: Reactive<Self> {
        get {
            return Reactive(self)
        }
        // swiftlint:disable:next unused_setter_value
        set {
            // this enables using Reactive to "mutate" base object
        }
    }
}

extension NSObject: ReactiveCompatible { }

// 擴展Reactive, 為CLLocationManager添加rx名稱空間
public extension Reactive where Base: CLLocationManager {}

2. 為rx添加可觀察的存儲屬性

public extension Reactive where Base: CLLocationManager {
  var didUpdateLocations: Observable<[CLLocation]> {
    
  }
}

3. 當CLLocationManager的代理接收到數(shù)據(jù)時髓考,可觀察對象發(fā)起事件給訂閱者

public extension Reactive where Base: CLLocationManager {
  var delegate: DelegateProxy<CLLocationManager, CLLocationManagerDelegate> {
        RxCLLocationManagerDelegateProxy.proxy(for: base)
    }
  
  var didUpdateLocations: Observable<[CLLocation]> {
    delegate
    .methodInvoked(
      #selector(CLLocationManagerDelegate.locationManager(_:didUpdateLocations:))
    )
    .map { parameters in
      parameters[1] as! [CLLocation]
    }
  }
}

4. DelegateProxy是什么

DelegateProxy.png

The DelegateProxy object creates a fake delegate object, which will proxy all the data received into dedicated observables

定義委托代理RxCLLocationManagerDelegateProxy, 代理接收到的數(shù)據(jù)給可觀察序列

RxCLLocationManagerDelegateProxy

class RxCLLocationManagerDelegateProxy: DelegateProxy<CLLocationManager, CLLocationManagerDelegate>, DelegateProxyType, CLLocationManagerDelegate {

    weak public private(set) var locationManager: CLLocationManager?

    public init(locationManager: ParentObject) {
        self.locationManager = locationManager
        super.init(parentObject: locationManager, delegateProxy: RxCLLocationManagerDelegateProxy.self)
    }

    static func registerKnownImplementations() {
        register {
            RxCLLocationManagerDelegateProxy(locationManager: $0)
        }
    }
}

5. DelegateProxy如何代理CLLocationManager的delegate接收到的數(shù)據(jù)

  1. RxCLLocationManagerDelegateProxy實例的創(chuàng)建過程

    let locationManager = CLLocationManager.init()
    locationManager.rx.delegate即為RxCLLocationManagerDelegateProxy的實例
    // DelegateProxyType.swift
    // 通過存儲屬性delegate的get方法, 該委托代理的實例化是通過RxCLLocationManagerDelegateProxy.proxy(for: base)實現(xiàn)的
    // 1. RxCLLocationManagerDelegateProxy的實例是作為CLLocationManager實例的關聯(lián)屬性保存的
    // 2. ParentObject為CLLocationManager的實例對象
    public static func proxy(for object: ParentObject) -> Self {
      // 1. 獲取CLLocationManager實例對象標識為self.identifier的關聯(lián)屬性
      let maybeProxy = self.assignedProxy(for: object)
      let proxy: AnyObject
      if let existingProxy = maybeProxy {
        // 2. 如果該關聯(lián)屬性存在, 則返回該關聯(lián)屬性
        proxy = existingProxy
      } else {
        // 3. 如果該關聯(lián)屬性不存在, 則創(chuàng)建該實例
        proxy = castOrFatalError(self.createProxy(for: object))
        // 4. 保存為CLLocationManager實例的關聯(lián)屬性
        self.assignProxy(proxy, toObject: object)
      }
      let currentDelegate = self._currentDelegate(for: object)
      let delegateProxy: Self = castOrFatalError(proxy)
      return delegateProxy
    }
    
    • 創(chuàng)建RxCLLocationManagerDelegateProxy的實例

      // DelegateProxyType.swift
      // object為CLLocationManager的實例對象
      public static func createProxy(for object: AnyObject) -> Self {
        return castOrFatalError(factory.createProxy(for: object))
      }
      
    • factory是什么

      // DelegateProxyType.swift
      extension DelegateProxyType {
        // 1. factory是DelegateProxyType擴展的靜態(tài)存儲屬性
          private static var factory: DelegateProxyFactory {
            // self為RxCLLocationManagerDelegateProxy類對象, 它遵守DelegateProxyType協(xié)議
              return DelegateProxyFactory.sharedFactory(for: self)
          }
      }
      
      // 這里使用了工廠模式, _sharedFactories里保存了各DelegateProxyType類所對應的工廠實例DelegateProxyType
      private class DelegateProxyFactory {
        private static var _sharedFactories: [UnsafeRawPointer: DelegateProxyFactory] = [:]
        // proxyType為RxCLLocationManagerDelegateProxy類對象
        fileprivate static func sharedFactory<DelegateProxy: DelegateProxyType>(for proxyType: DelegateProxy.Type) -> DelegateProxyFactory {
          // 泛型DelegateProxy即為RxCLLocationManagerDelegateProxy
          let identifier = DelegateProxy.identifier
          if let factory = _sharedFactories[identifier] {
            return factory
          }
          let factory = DelegateProxyFactory(for: proxyType)
          _sharedFactories[identifier] = factory
          // 保存DelegateProxy的子類到factory
          DelegateProxy.registerKnownImplementations()
          return factory
        }
      }
      
      • registerKnownImplementations

        • 作用

          注冊子類RxCLLocationManagerDelegateProxy的實例化閉包

        • 調(diào)用鏈路

          private class DelegateProxyFactory {
            fileprivate static func sharedFactory<DelegateProxy: DelegateProxyType>(for proxyType: DelegateProxy.Type) -> DelegateProxyFactory {
              ...
              DelegateProxy.registerKnownImplementations()
              ...
            }
          }
          
          class RxCLLocationManagerDelegateProxy: DelegateProxy<CLLocationManager, CLLocationManagerDelegate>, DelegateProxyType, CLLocationManagerDelegate {
            static func registerKnownImplementations() {
                  register {
                      RxCLLocationManagerDelegateProxy(locationManager: $0)
                  }
              }
          }
          
          extension DelegateProxyType {
            // Parent的類型是CLLocationManager
            public static func register<Parent>(make: @escaping (Parent) -> Self) {
              self.factory.extend(make: make)
            }
          }
          
          // ParentObject的類型是CLLocationManager, 閉包的返回類型是DelegateProxy的子類RxCLLocationManagerDelegateProxy
          func extend<DelegateProxy: DelegateProxyType, ParentObject>(make: @escaping (ParentObject) -> DelegateProxy) {
            ...
            // 這里factory的key是類對象CLLocationManager的identifier
            self._factories[ObjectIdentifier(ParentObject.self)] 
            = {make(castOrFatalError($0)) }
            ...
          }
          

      通過以上調(diào)用,DelegateProxyFactory的實例在調(diào)用createProxy方法時舞痰,就能獲取到相應的閉包土榴,返回閉包所創(chuàng)建的實例對象RxCLLocationManagerDelegateProxy

    • 工廠DelegateProxyFactory如何創(chuàng)建RxCLLocationManagerDelegateProxy實例

      private class DelegateProxyFactory {
        // object為CLLocationManager的實例對象
        fileprivate func createProxy(for object: AnyObject) -> AnyObject {
          var maybeMirror: Mirror? = Mirror(reflecting: object)
          while let mirror = maybeMirror {
            // 獲取注冊過的閉包
            if let factory = self._factories[ObjectIdentifier(mirror.subjectType)] {
              // 調(diào)用該閉包返回RxCLLocationManagerDelegateProxy的實例化對象
              return factory(object)
            }
            maybeMirror = mirror.superclassMirror
          }
        }
      }
      
  1. RxCLLocationManagerDelegateProxy實例成為CLLocationManager對象的代理

    public static func proxy(for object: ParentObject) -> Self {
      ...
      self._setCurrentDelegate(proxy, to: object)
      ...
    }
    
    extension DelegateProxyType where ParentObject: HasDelegate, Self.Delegate == ParentObject.Delegate {
      public static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject) {
        object.delegate = delegate
      }
    }
    
  2. 生成可觀察序列

    open class DelegateProxy<P: AnyObject, D>: _RXDelegateProxy {
      open func methodInvoked(_ selector: Selector) -> Observable<[Any]> {
        // 每個selector對應一個MessageDispatcher實例
         let subject = self._methodInvokedForSelector[selector]
         if let subject = subject {
          // _methodInvokedForSelector中存在
         return subject.asObservable()
         } else {
          // 不存在, 則實例化對應的MessageDispatcher實例并保存
         let subject = MessageDispatcher(selector: selector, delegateProxy: self)
         self._methodInvokedForSelector[selector] = subject
         return subject.asObservable()
         }
     }
    }
    
  3. 通過消息轉發(fā)機制,轉發(fā)對應的代理方法所獲取到的數(shù)據(jù)

    由于在實例化RxCLLocationManagerDelegateProxy的過程中响牛,已經(jīng)將RxCLLocationManagerDelegateProxy的實例作為CLLocationManager對象的delegate玷禽,但是并沒有提供對應代理方法的實現(xiàn),所有底層在通過代理調(diào)用相應的代理方法時呀打,會因為找不到對應的方法實現(xiàn)而進行消息轉發(fā)

    • 委托代理的繼承體系
    RxCLLocationManagerDelegateProxy:DelegateProxy:_RXDelegateProxy
    open class DelegateProxy<P: AnyObject, D>: _RXDelegateProxy {}
    
    @interface _RXDelegateProxy : NSObject
    @end
      
    @implementation _RXDelegateProxy
    @end
    
    • 生成事件轉發(fā)給訂閱者
    @implementation _RXDelegateProxy
    -(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];
        }
    }
    @end
    
    open class DelegateProxy<P: AnyObject, D>: _RXDelegateProxy {
      open override func _sentMessage(_ selector: Selector, withArguments arguments: [Any]) {
        // 獲取可觀察序列, 發(fā)送事件給訂閱者
        self._sentMessageForSelector[selector]?.on(.next(arguments))
      }
    
      open override func _methodInvoked(_ selector: Selector, withArguments arguments: [Any])     {
        // 獲取可觀察序列, 發(fā)送事件給訂閱者
        self._methodInvokedForSelector[selector]?.on(.next(arguments))
       }
    }
    

Extension a UIKit view

MKMapView, for example

Question:
1. 為什么在Rx中包裝一個具有返回類型的委托是一件困難的事情矢赁?
2. 怎樣處理帶有返回值的代理方法?

1. 為什么在Rx中包裝一個具有返回類型的委托是一件困難的事情贬丛?

  • 帶有返回類型的委托方法并不用于觀察撩银,而是用于自定義行為
  • 定義一個在任何情況下都可以工作的自動默認值不是一項簡單的任務

2. 怎樣處理帶有返回值的代理方法?

轉發(fā)帶有返回值的委托方法調(diào)用

forword.png

public extension Reactive where Base: MKMapView {
    var delegate: DelegateProxy<MKMapView, MKMapViewDelegate> {
      RxMKMapViewDelegateProxy.proxy(for: base)
    }

  // 轉發(fā)帶有返回值的委托方法調(diào)用給MKMapView
    func setDelegate(_ delegate: MKMapViewDelegate) -> Disposable {
        RxMKMapViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base)
    }

    var overlay:Binder<MKOverlay> {
        Binder(base) { mapView, overlay in
            mapView.removeOverlays(mapView.overlays)
            mapView.addOverlay(overlay)
        }
    }
}

// 上層以傳統(tǒng)方式遵守MKMapViewDelegate協(xié)議并實現(xiàn)其帶有返回值的委托方法
extension ViewController: MKMapViewDelegate {
  func mapView(_ mapView: MKMapView,
               rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    guard let overlay = overlay as? ApiController.Weather.Overlay else {
      return MKOverlayRenderer()
    }

    return ApiController.Weather.OverlayView(overlay: overlay,
                                             overlayIcon: overlay.icon)
  }
}

轉發(fā)過程的底層實現(xiàn)

調(diào)用鏈路

public extension Reactive where Base: MKMapView {
  func setDelegate(_ delegate: MKMapViewDelegate) -> Disposable {
        RxMKMapViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base)
    }
}
extension DelegateProxyType {
  // forwardDelegate為遵守MKMapViewDelegate協(xié)議并提供相應委托方法實現(xiàn)的對象豺憔,即上層的ViewController實例對象
  // ParentObject為MKMapView的實例
  public static func installForwardDelegate(_ forwardDelegate: Delegate, retainDelegate: Bool, onProxyForObject object: ParentObject) -> Disposable {
    weak var weakForwardDelegate: AnyObject? = forwardDelegate as AnyObject
    // proxy為委托代理對象额获,即RxMKMapViewDelegateProxy的實例
    let proxy = self.proxy(for: object)
    proxy.setForwardToDelegate(forwardDelegate, retainDelegate: retainDelegate)
    ...
  }
}
open func setForwardToDelegate(_ delegate: Delegate?, retainDelegate: Bool) {
  self._setForwardToDelegate(delegate, retainDelegate: retainDelegate)
}
@interface _RXDelegateProxy () {}
@implementation _RXDelegateProxy {
  // forwardToDelegate為遵守MKMapViewDelegate協(xié)議并提供相應委托方法實現(xiàn)的對象够庙,即上層的ViewController實例對象
  -(void)_setForwardToDelegate:(id __nullable)forwardToDelegate retainDelegate:(BOOL)retainDelegate {
    __forwardToDelegate = forwardToDelegate;
    if (retainDelegate) {
        self.strongForwardDelegate = forwardToDelegate;
    } else {
        self.strongForwardDelegate = nil;
    }
    }
}
  1. 生成事件轉發(fā)給訂閱者
@implementation _RXDelegateProxy
-(void)forwardInvocation:(NSInvocation *)anInvocation {
   ... 
    if (self._forwardToDelegate && [self._forwardToDelegate respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:self._forwardToDelegate];
    }
  ...
}
@end
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市抄邀,隨后出現(xiàn)的幾起案子耘眨,更是在濱河造成了極大的恐慌,老刑警劉巖境肾,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剔难,死亡現(xiàn)場離奇詭異,居然都是意外死亡奥喻,警方通過查閱死者的電腦和手機偶宫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來环鲤,“玉大人纯趋,你說我怎么就攤上這事⌒ń剩” “怎么了结闸?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長酒朵。 經(jīng)常有香客問我桦锄,道長,這世上最難降的妖魔是什么蔫耽? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任结耀,我火速辦了婚禮,結果婚禮上匙铡,老公的妹妹穿的比我還像新娘图甜。我一直安慰自己,他們只是感情好鳖眼,可當我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布黑毅。 她就那樣靜靜地躺著,像睡著了一般钦讳。 火紅的嫁衣襯著肌膚如雪矿瘦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天愿卒,我揣著相機與錄音缚去,去河邊找鬼。 笑死琼开,一個胖子當著我的面吹牛易结,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼搞动,長吁一口氣:“原來是場噩夢啊……” “哼躏精!你這毒婦竟也來了?” 一聲冷哼從身側響起滋尉,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤玉控,失蹤者是張志新(化名)和其女友劉穎飞主,沒想到半個月后狮惜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡碌识,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年碾篡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筏餐。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡开泽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出魁瞪,到底是詐尸還是另有隱情穆律,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布导俘,位于F島的核電站峦耘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏旅薄。R本人自食惡果不足惜辅髓,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望少梁。 院中可真熱鬧洛口,春花似錦、人聲如沸凯沪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妨马。三九已至挺举,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間身笤,已是汗流浹背豹悬。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留液荸,地道東北人瞻佛。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親伤柄。 傳聞我的和親對象是個殘疾皇子绊困,可洞房花燭夜當晚...
    茶點故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容