委托代理(delegate) iOS 開發中十分常見。不管是使用系統自帶的庫,還是一些第三方組件時,我們總能看到 delegate 的身影。使用 delegate 可以實現代碼的松耦合,減少代碼復雜度。但如果我們項目中使用 RxSwift,那么原先的 delegate 方式與我們鏈式編程方式就不相稱了。
解決辦法就是將代理方法進行一層 Rx 封裝,這樣做不僅會減少許多不必要的工作(比如原先需要遵守不同的代理,並且要實現相應的代理方法),還會使得代碼的聚合度更高,更加符合響應式編程的規范。
其實在 RxCocoa 源碼中我們也可以發現,它已經對標准的 Cocoa 做了大量的封裝(比如 tableView 的 itemSelected)。下面我將通過樣例演示如何將代理方法進行 Rx 化。
一、對 Delegate進行Rx封裝原理
1,DelegateProxy
(1)DelegateProxy 是代理委托,我們可以將它看作是代理的代理。
(2)DelegateProxy 的作用是做為一個中間代理,他會先把系統的 delegate 對象保存一份,然后攔截 delegate的方法。也就是說在每次觸發 delegate 方法之前,會先調用 DelegateProxy 這邊對應的方法,我們可以在這里發射序列給多個訂閱者。
2,流程圖
這里以 UIScrollView 為例,Delegate proxy 便是其代理委托,它遵守 DelegateProxyType 與 UIScrollViewDelegate,並能響應 UIScrollViewDelegate 的代理方法,這里我們可以為代理委托設計它所要響應的方法(即為訂閱者發送觀察序列)。
/*** +-------------------------------------------+ | | | 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) | | | +-------------------------------------------+ **/
二 自定義代理實現代理的一對多
1實現一個需要代理的類
//
// Car.swift
// Pod11
//
// Created by dzq_mac on 2020/1/13.
// Copyright © 2020 dzq_mac. All rights reserved.
//
import UIKit
import RxSwift
import CoreLocation
@objc public protocol CarOilProtocol: AnyObject {
@discardableResult
func oil80(name:String) -> String
@discardableResult
func oil50(name:String) -> String
// func oil20(whoCar:String)
// func oil10(whoCar:String)
}
extension CarOilProtocol {
public func oil10(whoCar:String){
}
public func oil20(whoCar:String){
}
}
public class CarCar:ReactiveCompatible {
var name :String
var oilQulity:Int32 = 100
var disposeBag:DisposeBag = DisposeBag()
var timer :Observable<Int>?
public weak var delegate:CarOilProtocol?
init(name:String) {
self.name = name
}
func startRun(){
timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
timer?.subscribe(onNext: {[weak self] (i) in
// print(i)
self?.oilQulity -= 1
if self?.oilQulity == 80 {
self?.delegate?.oil80(name: self?.name ?? "")
}else if self?.oilQulity == 50 {
self?.delegate?.oil50(name: self?.name ?? "")
}else{
}
}).disposed(by: disposeBag)
}
}
2.首先我們繼承 DelegateProxy 創建一個關於上述類代理委托,同時它還要遵守 DelegateProxyType 和 CarOilProtocol協議。
//
// CarCarOilProtocol.swift
// Pod11
//
// Created by dzq_mac on 2020/1/13.
// Copyright © 2020 dzq_mac. All rights reserved.
//
import UIKit
import RxSwift
import RxCocoa
import CoreLocation
extension CarCar :HasDelegate{
public typealias Delegate = CarOilProtocol
}
public class RxCarCarOilProtocolProxy: DelegateProxy<CarCar,CarOilProtocol>,DelegateProxyType,CarOilProtocol {
public init(car:CarCar) {
super.init(parentObject: car, delegateProxy: RxCarCarOilProtocolProxy.self)
}
public static func registerKnownImplementations() {
self.register { RxCarCarOilProtocolProxy(car: $0)}
}
internal lazy var oil80Subject = PublishSubject<String>()
internal lazy var oil50Subject = PublishSubject<String>()
public func oil80(name: String) -> String {
let nn = _forwardToDelegate?.oil80(name: name)//原來的代理
oil80Subject.onNext(name)
return nn ?? name
}
public func oil50(name: String) -> String {
let mm = _forwardToDelegate?.oil50(name: name)
oil50Subject.onNext(name)
return mm ?? name
}
deinit {
self.oil80Subject.on(.completed)
self.oil50Subject.on(.completed)
}
}
3.接着我們對 Carcar 進行Rx 擴展,作用是將Carcar與前面創建的代理委托關聯起來,將相關的 delegate 方法轉為可觀察序列。
//
// CarCar+rx.swift
// Pod11
//
// Created by dzq_mac on 2020/1/13.
// Copyright © 2020 dzq_mac. All rights reserved.
//
import UIKit
import RxSwift
import RxCocoa
extension Reactive where Base:CarCar{
public var delegate:DelegateProxy<CarCar,CarOilProtocol>{
return RxCarCarOilProtocolProxy.proxy(for: base)
}
public var dao80:Observable<String>{
return RxCarCarOilProtocolProxy.proxy(for: base).oil80Subject.asObserver()
// let source = delegate.methodInvoked(#selector(CarOilProtocol.oil80(name:)))
// .map{ s in
//
// return try castOrThrow1(String.self,s[1])
// }
// return source
}
public var dao50:Observable<String>{
return RxCarCarOilProtocolProxy.proxy(for: base).oil50Subject.asObserver()
}
public func setDelegate(_ delegate:CarOilProtocol) -> Disposable{
return RxCarCarOilProtocolProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base)
}
}
4在其他地方就可以像一般的RxSwift一樣訂閱這個序列了
var car : CarCar!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
car = CarCar(name: "beijing")
car.delegate = self
car.startRun()
car.rx.dao80.asObservable().subscribe({ (str) in
print("rx80--" + (str.element ?? ""))
}).disposed(by: disposeBag)
}
這樣寫,本來的代理的代理方法也會走,rx訂閱的方法也會走,就實現了從單一代理到多代理的轉化,RxSwift框架還是非常強大的,繼續學習,有興趣的可以一起交流啊!
