DelegateProxy的底层实现

以扩展CLLocationManager和MKMapView为例,探索DelegateProxy的底层实现

Extension CLLocationManager

Question:
1. 怎样为CLLocationManager的实例添加rx名称空间?
2. 为CLLocationManager的实例的rx名称空间里定义可观察存储属性
3. 这个可观察存储属性如何获取CLLocationManager的delegate接收到的数据并发起事件给订阅者?
4. DelegateProxy的作用是什么?
5. DelegateProxy如何代理CLLocationManager的delegate接收到的数据?

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的代理接收到数据时,可观察对象发起事件给订阅者

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, 代理接收到的数据给可观察序列

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接收到的数据

  1. RxCLLocationManagerDelegateProxy实例的创建过程

    let locationManager = CLLocationManager.init()
    locationManager.rx.delegate即为RxCLLocationManagerDelegateProxy的实例
    // DelegateProxyType.swift
    // 通过存储属性delegate的get方法, 该委托代理的实例化是通过RxCLLocationManagerDelegateProxy.proxy(for: base)实现的
    // 1. RxCLLocationManagerDelegateProxy的实例是作为CLLocationManager实例的关联属性保存的
    // 2. ParentObject为CLLocationManager的实例对象
    public static func proxy(for object: ParentObject) -> Self {
      // 1. 获取CLLocationManager实例对象标识为self.identifier的关联属性
      let maybeProxy = self.assignedProxy(for: object)
      let proxy: AnyObject
      if let existingProxy = maybeProxy {
        // 2. 如果该关联属性存在, 则返回该关联属性
        proxy = existingProxy
      } else {
        // 3. 如果该关联属性不存在, 则创建该实例
        proxy = castOrFatalError(self.createProxy(for: object))
        // 4. 保存为CLLocationManager实例的关联属性
        self.assignProxy(proxy, toObject: object)
      }
      let currentDelegate = self._currentDelegate(for: object)
      let delegateProxy: Self = castOrFatalError(proxy)
      return delegateProxy
    }
    
    • 创建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扩展的静态存储属性
          private static var factory: DelegateProxyFactory {
            // self为RxCLLocationManagerDelegateProxy类对象, 它遵守DelegateProxyType协议
              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的实例化闭包

        • 调用链路

          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)) }
            ...
          }
          

      通过以上调用,DelegateProxyFactory的实例在调用createProxy方法时,就能获取到相应的闭包,返回闭包所创建的实例对象RxCLLocationManagerDelegateProxy

    • 工厂DelegateProxyFactory如何创建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)] {
              // 调用该闭包返回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. 通过消息转发机制,转发对应的代理方法所获取到的数据

    由于在实例化RxCLLocationManagerDelegateProxy的过程中,已经将RxCLLocationManagerDelegateProxy的实例作为CLLocationManager对象的delegate,但是并没有提供对应代理方法的实现,所有底层在通过代理调用相应的代理方法时,会因为找不到对应的方法实现而进行消息转发

    • 委托代理的继承体系
    RxCLLocationManagerDelegateProxy:DelegateProxy:_RXDelegateProxy
    open class DelegateProxy<P: AnyObject, D>: _RXDelegateProxy {}
    
    @interface _RXDelegateProxy : NSObject
    @end
      
    @implementation _RXDelegateProxy
    @end
    
    • 生成事件转发给订阅者
    @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]) {
        // 获取可观察序列, 发送事件给订阅者
        self._sentMessageForSelector[selector]?.on(.next(arguments))
      }
    
      open override func _methodInvoked(_ selector: Selector, withArguments arguments: [Any])     {
        // 获取可观察序列, 发送事件给订阅者
        self._methodInvokedForSelector[selector]?.on(.next(arguments))
       }
    }
    

Extension a UIKit view

MKMapView, for example

Question:
1. 为什么在Rx中包装一个具有返回类型的委托是一件困难的事情?
2. 怎样处理带有返回值的代理方法?

1. 为什么在Rx中包装一个具有返回类型的委托是一件困难的事情?

  • 带有返回类型的委托方法并不用于观察,而是用于自定义行为
  • 定义一个在任何情况下都可以工作的自动默认值不是一项简单的任务

2. 怎样处理带有返回值的代理方法?

转发带有返回值的委托方法调用

forword.png

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

  // 转发带有返回值的委托方法调用给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)
        }
    }
}

// 上层以传统方式遵守MKMapViewDelegate协议并实现其带有返回值的委托方法
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)
  }
}

转发过程的底层实现

调用链路

public extension Reactive where Base: MKMapView {
  func setDelegate(_ delegate: MKMapViewDelegate) -> Disposable {
        RxMKMapViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base)
    }
}
extension DelegateProxyType {
  // forwardDelegate为遵守MKMapViewDelegate协议并提供相应委托方法实现的对象,即上层的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协议并提供相应委托方法实现的对象,即上层的ViewController实例对象
  -(void)_setForwardToDelegate:(id __nullable)forwardToDelegate retainDelegate:(BOOL)retainDelegate {
    __forwardToDelegate = forwardToDelegate;
    if (retainDelegate) {
        self.strongForwardDelegate = forwardToDelegate;
    } else {
        self.strongForwardDelegate = nil;
    }
    }
}
  1. 生成事件转发给订阅者
@implementation _RXDelegateProxy
-(void)forwardInvocation:(NSInvocation *)anInvocation {
   ... 
    if (self._forwardToDelegate && [self._forwardToDelegate respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:self._forwardToDelegate];
    }
  ...
}
@end
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容