平常我們使用 RxSwift 的時候,一般不會去直接使用 delegate,譬如要處理 tableView 的點(diǎn)擊事件,我們會這樣:tableView.rx.itemSelected.subscribe(onNext: handleSelectedIndexPath),這跟先設(shè)置一個 delegate,然后在 delegate 的tableView(_:didSelectRowAt:)方法中調(diào)用handleSelectedIndexPath的效果是一樣的。那這個過程到底是如何進(jìn)行的呢?我們進(jìn)入 RxCocoa 的 UITableView+Rx.swift 文件來一探究竟,這個文件中不僅有itemSelected,還有諸如itemDeselected、itemAccessoryButtonTapped、itemInserted、itemDeleted、itemMoved等等一系列對應(yīng) tableView delegate 的包裝方法,本文就以itemSelected為例,其他的都是相同的原理。為便于理解,我會給源碼加一點(diǎn)中文注釋,:
/**
Reactive wrapper for `delegate` message `tableView:didSelectRowAtIndexPath:`.
*/
public var itemSelected: ControlEvent<IndexPath> {
// delegate: PublishSubject<[AnyObject]>,[AnyObject]表示 selector 的參數(shù)列表
let source = self.delegate.observe(#selector(UITableViewDelegate.tableView(_:didSelectRowAt:)))
.map { a in
// 轉(zhuǎn)化第二個參數(shù)的類型為 IndexPath
return try castOrThrow(IndexPath.self, a[1])
}
// 包裝成一個 ControlEvent 返回,ControlEvent 其實只是在 source 外套了層殼,保證操作會在主線程進(jìn)行而已
return ControlEvent(events: source)
}
這個方法是寫在extension Reactive where Base: UITableView里的,想必大家應(yīng)該清楚這只是為了給擴(kuò)展加一個命名空間,Reactive<Base>是一個范型 struct,它有一個 base 屬性,Reactive 對外暴露的方法實際上都會轉(zhuǎn)發(fā)給 base。這塊如果大家不清楚的話可以看一下 Reactive.swift 文件,由于不是本文的重點(diǎn)就不細(xì)說了,可以理解為extension Reactive where Base: UITableView中的方法其實就是給UITableView加的擴(kuò)展方法。
DelegateProxy
幾個關(guān)鍵的地方我都加了中文注釋,大家應(yīng)該能明白。值得注意的是,這個方法里出現(xiàn)的self.delegate屬性并不在本文件中,那我們推測應(yīng)該是在別的 Reactive extension 中,跳到 UIScrollView+Rx.swift 看一下,果不其然:
extension Reactive where Base: UIScrollView {
// ...
public var delegate: DelegateProxy {
return RxScrollViewDelegateProxy.proxyForObject(base)
}
// ...
原來 delegate 啊是一個 DelegateProxy 類型(代理的代理^ ^)……這個proxyForObject方法顯然是接收一個對象(本文中這個對象是個 tableView 實例),然后返回其代理。那我們看一下 RxScrollViewDelegateProxy 這個類,卻并沒有發(fā)現(xiàn)proxyForObject這個方法,這時我看了眼它的類聲明:
/**
For more information take a look at `DelegateProxyType`.
*/
public class RxScrollViewDelegateProxy
: DelegateProxy
, UIScrollViewDelegate
, DelegateProxyType {
注釋上寫著 “For more information take a look at DelegateProxyType.”,想必我們能從 DelegateProxyType 這個協(xié)議里發(fā)現(xiàn)點(diǎn)什么,嗯,果然在這個協(xié)議的 extension 里發(fā)現(xiàn)了proxyForObject:
extension DelegateProxyType {
/**
Returns existing proxy for object or installs new instance of delegate proxy.
- parameter object: Target object on which to install delegate proxy.
- returns: Installed instance of delegate proxy.
*/
public static func proxyForObject(_ object: AnyObject) -> Self {
MainScheduler.ensureExecutingOnScheduler()
let maybeProxy = Self.assignedProxyFor(object) as? Self
let proxy: Self
if let existingProxy = maybeProxy {
proxy = existingProxy
}
else {
proxy = Self.createProxyForObject(object) as! Self
Self.assignProxy(proxy, toObject: object)
assert(Self.assignedProxyFor(object) === proxy)
}
let currentDelegate: AnyObject? = Self.currentDelegateFor(object)
if currentDelegate !== proxy {
proxy.setForwardToDelegate(currentDelegate, retainDelegate: false)
assert(proxy.forwardToDelegate() === currentDelegate)
Self.setCurrentDelegate(proxy, toObject: object)
assert(Self.currentDelegateFor(object) === proxy)
assert(proxy.forwardToDelegate() === currentDelegate)
}
return proxy
}
這個方法看著很長,其核心是通過assignedProxyFor(object)去拿 tableView 實例的關(guān)聯(lián)代理 proxy,如果沒有的話,就先用createProxyForObject(object)創(chuàng)建一個代理,然后用assignProxy(proxy, toObject object)將 proxy 設(shè)置為 tableView 實例的關(guān)聯(lián)對象。如果這個 tableView 實例還未設(shè)置 delegate,就調(diào)用setCurrentDelegate(proxy, toObject: object)將 tableView 的 delegate 設(shè)置為 proxy,最后返回 proxy。這里使用的幾個方法并沒有在協(xié)議擴(kuò)展里實現(xiàn),而是分別在DelegateProxy和RxScrollViewDelegateProxy中實現(xiàn)的,先看DelegateProxy中:
public class func createProxyForObject(_ object: AnyObject) -> AnyObject {
return self.init(parentObject: object)
}
public class func assignedProxyFor(_ object: AnyObject) -> AnyObject? {
// 得到關(guān)聯(lián)代理
let maybeDelegate = objc_getAssociatedObject(object, self.delegateAssociatedObjectTag())
return castOptionalOrFatalError(maybeDelegate.map { $0 as AnyObject })
}
public class func assignProxy(_ proxy: AnyObject, toObject object: AnyObject) {
precondition(proxy.isKind(of: self.classForCoder()))
// 設(shè)置關(guān)聯(lián)代理
objc_setAssociatedObject(object, self.delegateAssociatedObjectTag(), proxy, .OBJC_ASSOCIATION_RETAIN)
}
這些都很好理解,然后setCurrentDelegate的實現(xiàn)在RxScrollViewDelegateProxy中,值得一提的是,createProxyForObject在RxScrollViewDelegateProxy中也被重寫了,我們來看一下:
public class RxScrollViewDelegateProxy
: DelegateProxy
, UIScrollViewDelegate
, DelegateProxyType {
// ...
public override class func createProxyForObject(_ object: AnyObject) -> AnyObject {
let scrollView = (object as! UIScrollView)
// 調(diào)用 UITableView 的 createRxDelegateProxy 方法,返回一個 RxTextViewDelegateProxy 實例
return castOrFatalError(scrollView.createRxDelegateProxy())
}
public class func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {
let scrollView: UIScrollView = castOrFatalError(object)
scrollView.delegate = castOptionalOrFatalError(delegate)
}
// ...
}
extension UITableView {
public override func createRxDelegateProxy() -> RxScrollViewDelegateProxy {
return RxTableViewDelegateProxy(parentObject: self)
}
// ...
對于 tableView 來說,createProxyForObject返回的實際上是一個RxTableViewDelegateProxy,我們看一下它的聲明:
public class RxTableViewDelegateProxy
: RxScrollViewDelegateProxy
, UITableViewDelegate
綁定 selector 和 subject
好的,現(xiàn)在我們已經(jīng)知道開頭itemSelected中出現(xiàn)的self.delegate是什么了,接下來看看self.delegate.observe又做了啥,我們回到DelegateProxy中:
open class DelegateProxy : _RXDelegateProxy {
private var subjectsForSelector = [Selector: PublishSubject<[AnyObject]>]()
// ...
// 將 selector(返回值是 void) 和一個 subject 關(guān)聯(lián),通過 subject 發(fā)送事件給 obsevers
public func observe(_ selector: Selector) -> Observable<[AnyObject]> {
if hasWiredImplementation(for: selector) {
print("Delegate proxy is already implementing `\(selector)`, a more performant way of registering might exist.")
}
if !self.responds(to: selector) {
rxFatalError("This class doesn't respond to selector \(selector)")
}
// 已經(jīng)存在對應(yīng)這個 selector 的 subject
let subject = subjectsForSelector[selector]
if let subject = subject {
return subject
}
else {
// 尚未創(chuàng)建該 selector 對應(yīng)的 subject,先創(chuàng)建一個
let subject = PublishSubject<[AnyObject]>()
// 緩存到字典中
subjectsForSelector[selector] = subject
return subject
}
}
// ...
注釋已經(jīng)寫清楚了,這個方法第一次會把 selector 和一個新建的 subject 綁定,緩存到字典中,之后就通過 selector 來取對應(yīng)的 subject。接著我在這個方法的下面看到了另一個方法:
// 父類 _RXDelegateProxy 重寫了 forwardInvocation 方法,forwardInvocation 中會調(diào)用本方法
open override func interceptedSelector(_ selector: Selector, withArguments arguments: [Any]) {
// selector 對應(yīng)的 subject 發(fā)送一個包含 selector 參數(shù)列表的事件
subjectsForSelector[selector]?.on(.next(arguments as [AnyObject]))
}
這個方法接收一個 selector 和其參數(shù)列表,以 selector 為 key 找到對應(yīng)的 subject,subject 發(fā)射一個包含 selector 參數(shù)列表的 next 事件。顯然這個方法的調(diào)用時機(jī)是個關(guān)鍵,這里就用到了 Runtime 的消息轉(zhuǎn)發(fā)(Runtime 相關(guān)的東西網(wǎng)上有很多資料,也不是本文的重點(diǎn),我就不細(xì)說了),我在注釋里也寫了,DelegateProxy的父類_RXDelegateProxy重寫了forwardInvocation方法,在里面調(diào)用了interceptedSelector方法。這樣一來,當(dāng)某個 selector 要被調(diào)用時,由于 proxy 對象沒有對應(yīng)實現(xiàn),最后會走 forwardInvocation 把消息轉(zhuǎn)發(fā)給 interceptedSelector,對應(yīng)的 subject 發(fā)送包含參數(shù)列表的事件給所有 observer,整個過程就走通了。
小結(jié)
我在文中講述了自己閱讀源碼的心路歷程,如何按圖索驥,一步步理清整個過程,興許對那些想要閱讀源碼卻不知如何入手的朋友會有幫助。
水平有限,如有錯漏,歡迎指出~