RxDataSource 中数据源是如何和绑定的-3

RxDataSource(3.0.1)版
RxDataSource 中数据源是如何和绑定的--tableView.rx.items: 第三种重载实现
《2》中分析了tableView.rx.items第一种重载的实现,现在分析一下第三种重载实现

public func items<DataSource: RxTableViewDataSourceType & UITableViewDataSource, O: ObservableType>
        (dataSource: DataSource)        //函数参数类型:RxTableViewReactiveArrayDataSourceSequenceWrapper
        -> (_ source: O) -> Disposable  //函数返回类型
        where DataSource.Element == O.E {
    return { source in

        //1、先获取一下代理,确保存在(若不存在内部会创建并保存起来)
        _ = self.delegate
        
        //2、内部设置了tableView.dataSource,并订阅了source自己,nextEvent到来会执行后面的尾随闭包
        return source.subscribeProxyDataSource(ofObject: self.base, dataSource: dataSource as UITableViewDataSource, retainDataSource: true) { [weak tableView = self.base] (_: RxTableViewDataSourceProxy, event) -> Void in
            guard let tableView = tableView else {
                return
            }
            //3、每次数据源有新数据到来时都会执行
            //里面做的事情: 保存到来的数据,然后tableView.reload
            dataSource.tableView(tableView, observedEvent: event)
        }
    }
}

首先它是一个f(a) -> ( f(b) -> c)类型的函数,return的 { source in ..... } 闭包 就是 f(b) -> c 类型的函数。

代码片段中进行了基本的注释解释,然后分步看一下。

1、_ = self.delegate 
就这么短短的一行,却做了很多事情,这个delegate它负责tableview的代理事件,这个delegate你在tableview+rx里面找不到,因为它是来自scrollView+rx中的一个计算型属性:

/// For more information take a look at `DelegateProxyType` protocol documentation.
public var delegate: DelegateProxy<UIScrollView, UIScrollViewDelegate> {
    return RxScrollViewDelegateProxy.proxy(for: base)
}

 RxScrollViewDelegateProxy.proxy(for: base), base代表了tableview它自己,
RxScrollViewDelegateProxy 可以看成是一个特殊的字典,根据base 取值,内部实现如下:

// delegate和dataSource都用该方法获取/设置
    public static func proxy(for object: ParentObject) -> Self {
        MainScheduler.ensureExecutingOnScheduler()
        //获取关联的代理/数据源
        let maybeProxy = self.assignedProxy(for: object)
        
        // Type is ideally be `(Self & Delegate)`, but Swift 3.0 doesn't support it.
        let proxy: Delegate
        // 存在
        if let existingProxy = maybeProxy {
            proxy = existingProxy
        }
        else {
            // 不存在,创建代理,
            proxy = castOrFatalError(self.createProxy(for: object))
            // 设置关联
            self.assignProxy(proxy, toObject: object)
            assert(self.assignedProxy(for: object) === proxy)
        }
        // 获取object(tableView.delegate,dataSource)
        let currentDelegate = self.currentDelegate(for: object)
        // 确认proxy有值
        let delegateProxy: Self = castOrFatalError(proxy)
        
        // tableView.delegate被设置过,且和proxy不是同一个对象
        if currentDelegate !== delegateProxy {
            //把tableView.delegate存起来(叫做ForwardToDelegate)
            delegateProxy.setForwardToDelegate(currentDelegate, retainDelegate: false)
            assert(delegateProxy.forwardToDelegate() === currentDelegate)
            //根据self是什么类型而定 数据源类型or代理类型 设置 tableView.delegate = proxy 或 tableView.dataSource = proxy
            self.setCurrentDelegate(proxy, to: object)
            assert(self.currentDelegate(for: object) === proxy)
            assert(delegateProxy.forwardToDelegate() === currentDelegate)
        }
        // 返回被替换的代理对象
        return delegateProxy
    }

这是一个很重要的方法,查找数据源和代理都用该方法,
public static func proxy(for object: ParentObject) -> Self 这个方法是DelegateProxyType协议的extension实现的

那么如何知道是要获取数据源还是获取代理呢,其实是根据self的不同,从而获取到不同的内容,这里的self 是RxScrollViewDelegateProxy类型,当获取数据源时,self会是其他类型,但是他们都实现了DelegateProxyType协议,所以用的时候,各取各的。

let maybeProxy = self.assignedProxy(for: object),点击进入self.assignedProxy(for: object)实现,

    fileprivate static func assignedProxy(for object: ParentObject) -> Delegate? {
        let maybeDelegate = objc_getAssociatedObject(object, self.identifier)
        return castOptionalOrFatalError(maybeDelegate.map { $0 as AnyObject })
    }

    fileprivate static func assignProxy(_ proxy: Delegate, toObject object: ParentObject) {
        objc_setAssociatedObject(object, self.identifier, proxy, .OBJC_ASSOCIATION_RETAIN)
    }

这个函数设置和获取代理时,用到了runtime的关联方法,将它自身创建的代理和数据源关联到了tableView身上,使用的key 是用self (RxScrollViewDelegateProxy或者数据源代理类型RxTableViewDataSourceProxy)产生一个标识。这个identifier是如何产生的可以自己查看。

检查tableView.delegate 是否被人为设置过原始delegate,如果有,它会保存起来。然后它将自己的创建的代理proxy设置给tableview.delegate,proxy代理执行时,它会同步通知原始delegate。

这里也会产生一个小问题,就是如果我们同时使用rx和设置tableview.delegate = self(xxx)时,要保证我们自己写的tableview.delegate = self(xxx)在设置rx的前面,否则rx会失效

2、 return source.subscribeProxyDataSource( .......
这个返回类型其实是一个Disposable

return source.subscribeProxyDataSource(ofObject: self.base,
                                       dataSource: dataSource as UITableViewDataSource,
                                       retainDataSource: true)
{ [weak tableView = self.base] (_: RxTableViewDataSourceProxy, event) -> Void in
    guard let tableView = tableView else {
        return
    }
    dataSource.tableView(tableView, observedEvent: event)
}

subscribeProxyDataSource函数的原型:

func subscribeProxyDataSource<DelegateProxy: DelegateProxyType>(ofObject object: DelegateProxy.ParentObject,
                                                                dataSource: DelegateProxy.Delegate,
                                                                retainDataSource: Bool,
                                                                binding: @escaping (DelegateProxy, Event<E>) -> Void)
    -> Disposable

在source执行完subscribeProxyDataSource后正好返回了一个 Disposable.
subscribeProxyDataSource 函数 binding 其实是一个尾闭包。

类型:binding: @escaping (DelegateProxy, Event<E>) -> Void)

 所以导致我们看到调用source.subscribeProxyDataSource的写法有点别扭了。下面换种形式

return source.subscribeProxyDataSource(ofObject: self.base,
                                       dataSource: dataSource as UITableViewDataSource,
                                       retainDataSource: true)
{ [weak tableView = self.base] (_: RxTableViewDataSourceProxy, event) -> Void in
    guard let tableView = tableView else {
        return
    }
    dataSource.tableView(tableView, observedEvent: event)
}

换成普通形式:
typealias BindingType = @escaping (DelegateProxy, Event<E>) -> Void
let binding: BindingType =
            { (_: RxTableViewDataSourceProxy, event) -> Void in
                guard let tableView = tableView else {
                    return
                }
                dataSource.tableView(tableView, observedEvent: event)
            }
return source.subscribeProxyDataSource(ofObject: self.base,
                           dataSource: dataSource as UITableViewDataSource,
                           retainDataSource: true,
                           binding: binding)

其中:

 (_: RxTableViewDataSourceProxy, event) -> Void

这里_: RxTableViewDataSourceProxy 忽略了值,只限定了类型

这里传入的类型,就和之前获取代理还是数据源产生了联系了,此处binding传入的闭包函数,类型限定了RxTableViewDataSourceProxy,所以导致subscribeProxyDataSource函数内实现时,
let proxy = DelegateProxy.proxy(for: object)获取到的就是数据源DataSource,而不是delegate。
不光是这里做了类型限制,打开DelegateProxyType文件:

public protocol DelegateProxyType: class {
    associatedtype ParentObject: AnyObject  
    associatedtype Delegate: AnyObject    //  这里也做了一些限制,真正实现时,这里会被实现协议的class落实成UIScrollViewDelegate或者UItableViewDataSoucrce 等
    .....
}

再来看RxScrollViewDelegateProxy和RxTableViewDataSourceProxy 文件

open class RxScrollViewDelegateProxy
    : DelegateProxy<UIScrollView, UIScrollViewDelegate> 
    , DelegateProxyType 
    , UIScrollViewDelegate {
......
}

open class RxTableViewDataSourceProxy
    : DelegateProxy<UITableView, UITableViewDataSource>
    , DelegateProxyType 
    , UITableViewDataSource {
.....
}

//上面两个都是继承DelegateProxy
open class DelegateProxy<P: AnyObject, D: AnyObject>: _RXDelegateProxy {
        public typealias ParentObject = P
        public typealias Delegate = D
}

实现DelegateProxyType协议的class,在定义时就定义好了DelegateProxyType 中用到的两种类型,所以说,他们虽然都用的事同一套方法获取代理,但是由于类型不同,获取到关联的内容也就不同。

猜你喜欢

转载自my.oschina.net/dahuilang123/blog/1634788