RxCocoa

来源:互联网 发布:adobe flash mac 编辑:程序博客网 时间:2024/06/11 03:32

DelegateProxy

由英文意思就明白,委托代理,但是需要注意:该类的实现并不是安全的,目前只能用于主线程。

DelegateProxyType

DelegateProxyType协议允许使用正常的代理和Rx可观察者序列视图,并且只有唯一的delegate/datasource 被注册。DelegateProxyType协议应该从未直接被初始化,为了获取DelegateProxyType类型实例,应该使用proxyForObject方法。简单的工作原理如下:



为了将 delegate 中每个逻辑分解出来,我们需要建立一个中间代理,每次触发 delegate 时,都先触发这个代理,然后这个代理作为一个序列发射值给多个订阅者,这样我们就将不相关的逻辑分开了,不需要将所有的逻辑都写在同一个 delegate 中。事实上 RxCocoa 已经为我们提供了一些 proxy :



为什么需要DelegateProxy&DelegateProxyType?

因为项目中使用了RxSwift,如果我们想进行链式操作,并且又涉及到使用代理,那么使用代理的模式将并部适合我们的链式处理操作,所以RxCocoa为我们提供了DelegateProxy来处理代理,这样能够更好的进行响应式编程。

Tableview的点击事件实现

  /**     Reactive wrapper for `delegate` message `tableView:didDeselectRowAtIndexPath:`.     */    public var itemDeselected: ControlEvent<IndexPath> {        let source = self.delegate.methodInvoked(#selector(UITableViewDelegate.tableView(_:didDeselectRowAt:)))            .map { a in                return try castOrThrow(IndexPath.self, a[1])            }        return ControlEvent(events: source)    }

它实际上是使用了methodInvoked方法,该方法传入对应的代理方法,每当代理方法被调用,会触发发送对应的事件,然后封装为对应的ControlEvent。所以每次我们点击cell,会触发对应的代理方法,进而触发观察者序列发送事件,并且携带对应的位置作为参数,只要我们使用该属性进行订阅,我们就可以获取对应的点击位置。所以,我们也可以按照这种方式进行处理代理方法。


自定义代理实现


无返回值的实现

功能描述:我们常常需要用户的位置,所以实现获取用户的当前位置
1:首先创建一个CLLocationManager+Rx.swift文件,然后导入CoreLocation、RxSwift、 RxCocoa等框架,文件如下:


2:所有的扩展都是在.rx命名空间之后,我们可以先找到RxSwift下Reactive.swift文件查看一下,为什么我们的类能够使用.rx,打开文件,源码如下:

/** Use `Reactive` proxy as customization point for constrained protocol extensions. General pattern would be: // 1. Extend Reactive protocol with constrain on Base // Read as: Reactive Extension where Base is a SomeType extension Reactive where Base: SomeType { // 2. Put any specific reactive extension for SomeType here } With this approach we can have more specialized methods and properties using `Base` and not just specialized on common base type. */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    }}/// A type that has reactive extensions.public protocol ReactiveCompatible {    /// Extended type    associatedtype CompatibleType    /// Reactive extensions.    static var rx: Reactive<CompatibleType>.Type { get set }    /// Reactive extensions.    var rx: Reactive<CompatibleType> { get set }}extension ReactiveCompatible {    /// Reactive extensions.    public static var rx: Reactive<Self>.Type {        get {            return Reactive<Self>.self        }        set {            // this enables using Reactive to "mutate" base type        }    }    /// Reactive extensions.    public var rx: Reactive<Self> {        get {            return Reactive(self)        }        set {            // this enables using Reactive to "mutate" base object        }    }}import class Foundation.NSObject/// Extend NSObject with `rx` proxy.extension NSObject: ReactiveCompatible { }
由上面可知,文件中有Reactive<Base>结构体,ReactiveCompatible协议和ReactiveCompatible的扩展,扩展中包含了rx属性。特别是最后一行代码,为NSObject类添加扩展遵守ReactiveCompatible协议,这就是为什么每一个继承于NSObject的类都能够使用.rx命名空间。

3:RxSwift提供的Ractive proxy模式,最主要的部分就是RxCocoa目录下的oc文件_RxDelegateProxy.h和 _RxDelegateProxy.m以及swift文件DelegateProxy.swift和DelegateProxyType.swift。这些文件中包含了RxSwift与任意框架的桥梁实现,能够使用delegate(data sources)作为主要的资源提供数据。DelegateProxy对象创建了假的代理对象,该代理对象将为观察者序列接收所有的数据。使用CCLocationManager来对比理解,图形如下:



组合DelegateProxy和Reactive,将使我们的CLLocationManager扩展看起来跟所有官方RxCocoa extensions一样。因为CLLocationManager需要一个代理,所以我们需要重建一个必要的代理来获取数据,从locationManager的委托代理到专门的观察者序列。映射非常简单,一对一的关系,单一的协议函数将对应单一的观察者序列并且返回值。下面开始编写代码,进入CLLocationManager+Rx.swift文件,实现如下:

class RxCLLocationManagerDelegateProxy: DelegateProxy,CLLocationManagerDelegate, DelegateProxyType {    class func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {        let locationManager: CLLocationManager = object as! CLLocationManager        locationManager.delegate = delegate as? CLLocationManagerDelegate    }        class func currentDelegateFor(_ object: AnyObject) -> AnyObject? {        let locationManager: CLLocationManager = object as! CLLocationManager        return locationManager.delegate    }}
1:RxCLLocationManagerDelegateProxy类即将成为我们的代理,只要我们的观察者序列被创建并订阅之后会关联到CLLocationManager的实例。
2:为委托代理实现DelegateProxyType协议中的setter和getter方法,通过这两个函数我们能够设置和获取代理,代理能够被用于获取数据来自CLLocationManager实例并且连接到观察者序列,这里实现了在RxCocoa中怎样使用自定义类实现代理模式

注意:基本的 Proxy 都是这样设置的,主要是要继承 DelegateProxy 和实现 DelegateProxyType ,别忘了加上我们自己实现类对应的代理,这里是 CLLocationManagerDelegate。

接下来扩展Reactive,为Reactive添加属性和方法,扩展中所有暴露的方法和属性都能够用于CLLocationManager的实例,增加didUpdateLocations属性观察位置的改变
extension Reactive where Base: CLLocationManager {    var delegate: DelegateProxy {        return RxCLLocationManagerDelegateProxy.proxyForObject(base)    }        var didUpdateLocations: Observable<[CLLocation]> {        return delegate.methodInvoked(#selector(CLLocationManagerDelegate.locationManager(_:didUpdateLocations:))).map { parameters in            return parameters[1] as! [CLLocation]        }    }}
1:获取委托代理,并使用创建的委托代理来获取创建观察者序列(observables)监听位置(location)的改变
2:监听didUpdateLocations属性,delegate将作为委托代理监听didUpdateLocations所有的调用,获取数据转换为数组[CLLocation],methodInvoked方法:是oc的代码,位于RxCocoa中,对代理进行观察。methodInvoked返回一个观察者序列并发送next事件,无论什么时候具体的代理方法被调用。元素(elements)被包含在方法参数中。我们能够访问参数,获取需要的数据,这里是一个数组,使用parameters[1] 获取数组内容并转换为[CLLocation]

到这里我们已经可以开始使用了,如:

//获取当前的位置locationManager.rx.didUpdateLocations    .map { locations in        return locations[0]    }    .filter { location in        return location.horizontalAccuracy < kCLLocationAccuracyHundredMeters    }.subscribe(onNext: { location in        print(location)    }).disposed(by: bag)
上面是不是很简单,如果需要其它功能,同样是声明属性,调用methodInvoked执行对应的代理方法,获取对应的数据,订阅属性获取需要的部分。

有返回值的实现

前面例子实现了执行对应的代理方法,但是代理方法并没有返回值,如果是有返回值的代理方法,我们应该如何处理呢?我们使用MKMapView作为一个例子来说明如何扩展 UIKit component。为了开始扩展MKMapView,我们将采取跟CLLocationManager相同的模式,创建一个代理RxMKMapViewDelegateProxy并且为MKMapView扩展Reactive。

1:自定义RxMKMapViewDelegateProxy类,继承于DelegateProxy并且遵守MKMapViewDelegate和DelegateProxyType协议

class RxMKMapViewDelegateProxy: DelegateProxy, MKMapViewDelegate, DelegateProxyType {    class func currentDelegateFor(_ object: AnyObject) -> AnyObject? {        let mapView: MKMapView = (object as? MKMapView)!        return mapView.delegate    }        class func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {        let mapView: MKMapView = (object as? MKMapView)!        mapView.delegate = delegate as? MKMapViewDelegate    }}
2:扩展Reactive,实现获取代理对象,通过代理对象监听区域的改变

extension Reactive where Base: MKMapView {        public var delegate: DelegateProxy {        return RxMKMapViewDelegateProxy.proxyForObject(base)    }        //通过该函数,现在我们可以设置转发函数( forwarding delegate ),而且能够被调用,也能够提供返回值    public func setDelegate(_ delegate: MKMapViewDelegate) -> Disposable {        return RxMKMapViewDelegateProxy.installForwardDelegate(delegate,                                                               retainDelegate: false,                                                               onProxyForObject: self.base)    }       //为代理实现更多便利的通知机制,监听用户的拖动事件和mapview的导航事件    public var reginDidChangeAnimated: ControlEvent<Bool> {        let source = delegate            .methodInvoked(#selector(MKMapViewDelegate.mapView(_:regionDidChangeAnimated:)))            .map { parameters in            return (parameters[1] as? Bool) ?? false        }        return ControlEvent(events: source)    }}

3:设置代理,并实现代理方法,然后可以直接使用了

//1mapView.rx.setDelegate(self)    .disposed(by: bag)//2extension ViewController: MKMapViewDelegate {    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) ->        MKOverlayRenderer {            //do something...            return MKOverlayRenderer()    }}//3mapView.rx.reginDidChangeAnimated    .map { _  in        self.mapView.centerCoordinate    }    .subscribe(onNext: { coordinate2D in        print(coordinate2D)    }).disposed(by: bag)

推荐:


RxDelegate

DelegateProxy.swift