delegate 如何转为 Observable
先从 UIScrollView 的扩展开始
extension Reactive where Base: UIScrollView {
public var didScroll: ControlEvent<Void> {
let source = RxScrollViewDelegateProxy.proxy(for: base).contentOffsetPublishSubject
return ControlEvent(events: source)
}
/// Reactive wrapper for delegate method `scrollViewWillBeginDecelerating`
public var willBeginDecelerating: ControlEvent<Void> {
let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillBeginDecelerating(_:))).map { _ in }
return ControlEvent(events: source)
}
}
如上所见分为两种方式将delegate事件 转为 Observable ,
- 对DelegateProxy 已有的 Observable 进行二次封装。
- 通过methodInvoked 方法 , 获得 Observable
先看看第一种怎么实现,我们一层层的看
open class RxScrollViewDelegateProxy
: DelegateProxy<UIScrollView, UIScrollViewDelegate>
, DelegateProxyType
, UIScrollViewDelegate {
internal var contentOffsetBehaviorSubject: BehaviorSubject<CGPoint> {
if let subject = _contentOffsetBehaviorSubject {
return subject
}
// 初始化
let subject = BehaviorSubject<CGPoint>(value: self.scrollView?.contentOffset ?? CGPoint.zero)
_contentOffsetBehaviorSubject = subject
return subject
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
// 代理scrollViewDidScroll 事件,并且发送消息
if let subject = _contentOffsetBehaviorSubject {
subject.on(.next(scrollView.contentOffset))
}
if let subject = _contentOffsetPublishSubject {
subject.on(.next(()))
}
self._forwardToDelegate?.scrollViewDidScroll?(scrollView)
}
deinit {
// 在deinit 时候,发出completed 事件
if let subject = _contentOffsetBehaviorSubject {
subject.on(.completed)
}
if let subject = _contentOffsetPublishSubject {
subject.on(.completed)
}
}
}
这个原理比较简单,重点看看第二种, 先看看methodInvoked 原型
open func methodInvoked(_ selector: Selector) -> Observable<[Any]>
public var willBeginDecelerating: ControlEvent<Void> {
let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillBeginDecelerating(_:))) // Observable<[Any]>
.map { _ in } // Observable<Void>
return ControlEvent(events: source)
}
}
暂时先不纠结methodInvoked 具体实现, 先看看delegate 是如何运作的?
@protocol Delegate {
@optional
... 可选方法
methd_one
}
a.delegate = oneDelegate
这里如果知道oneDelegate 如果没有实现 声明 methd_one 的话, 是不会进行消息转发的。 可是Rx并没有依赖这种机制, 那么Rx是怎么做到的, 答案就在于利用runtime 进行消息转发。 以下是个简单的demo
#import "ViewController.h"
#import <objc/runtime.h>
@interface CustomUIScrollViewDelegate: NSObject<UIScrollViewDelegate>
@end
@implementation CustomUIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// NSLog(@"%@", scrollView);
NSLog(@"*****");
}
@end
@interface ViewController ()<UIScrollViewDelegate> {
CustomUIScrollViewDelegate *_delegate;
}
@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// UITableView
_delegate = [[CustomUIScrollViewDelegate alloc]init];
self.scrollView.delegate = self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)respondsToSelector:(SEL)aSelector {
if( aSelector == @selector(scrollViewDidScroll:) && [_delegate respondsToSelector:aSelector]) {
return YES;
}
return NO;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
// [anInvocation invoke];
NSLog(@"forwardInvocation");
[anInvocation invokeWithTarget:_delegate];
}
@end
简单的说通过respondsToSelector 确定是否能够响应该method, 然后通过消息转发机制转发消息。 这里面有个关键就是如何确认哪些Selector 是能响应的 哪些是不能。
阅读一下 _RXDelegateProxy 源码 (_RXDelegateProxy是所有Proxy基类)
//
// _RXDelegateProxy.m
// RxCocoa
//
// Created by Krunoslav Zaher on 7/4/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#import "include/_RXDelegateProxy.h"
#import "include/_RX.h"
#import "include/_RXObjCRuntime.h"
@interface _RXDelegateProxy () {
id __weak __forwardToDelegate;
}
@property (nonatomic, strong) id strongForwardDelegate;
@end
static NSMutableDictionary *voidSelectorsPerClass = nil;
@implementation _RXDelegateProxy
// 递归收集协议里,返回值为void 方法 , 返回 NSSet<SEL>
+(NSSet*)collectVoidSelectorsForProtocol:(Protocol *)protocol {
NSMutableSet *selectors = [NSMutableSet set];
unsigned int protocolMethodCount = 0;
struct objc_method_description *pMethods = protocol_copyMethodDescriptionList(protocol, NO, YES, &protocolMethodCount);
for (unsigned int i = 0; i < protocolMethodCount; ++i) {
struct objc_method_description method = pMethods[i];
if (RX_is_method_with_description_void(method)) {
[selectors addObject:SEL_VALUE(method.name)];
}
}
free(pMethods);
unsigned int numberOfBaseProtocols = 0;
Protocol * __unsafe_unretained * pSubprotocols = protocol_copyProtocolList(protocol, &numberOfBaseProtocols);
for (unsigned int i = 0; i < numberOfBaseProtocols; ++i) {
[selectors unionSet:[self collectVoidSelectorsForProtocol:pSubprotocols[i]]];
}
free(pSubprotocols);
return selectors;
}
// 递归收集类和父类所支持协议中所有的 返回值为void 的方法
// voidSelectorsPerClass: NSDictionary<Class, NSSet<SEL>>
+(void)initialize {
@synchronized (_RXDelegateProxy.class) {
if (voidSelectorsPerClass == nil) {
voidSelectorsPerClass = [[NSMutableDictionary alloc] init];
}
NSMutableSet *voidSelectors = [NSMutableSet set];
#define CLASS_HIERARCHY_MAX_DEPTH 100
NSInteger classHierarchyDepth = 0;
Class targetClass = NULL;
for (classHierarchyDepth = 0, targetClass = self;
classHierarchyDepth < CLASS_HIERARCHY_MAX_DEPTH && targetClass != nil;
++classHierarchyDepth, targetClass = class_getSuperclass(targetClass)
) {
unsigned int count;
Protocol *__unsafe_unretained *pProtocols = class_copyProtocolList(targetClass, &count);
for (unsigned int i = 0; i < count; i++) {
NSSet *selectorsForProtocol = [self collectVoidSelectorsForProtocol:pProtocols[i]];
[voidSelectors unionSet:selectorsForProtocol];
}
free(pProtocols);
}
if (classHierarchyDepth == CLASS_HIERARCHY_MAX_DEPTH) {
NSLog(@"Detected weird class hierarchy with depth over %d. Starting with this class -> %@", CLASS_HIERARCHY_MAX_DEPTH, self);
#if DEBUG
abort();
#endif
}
voidSelectorsPerClass[CLASS_VALUE(self)] = voidSelectors;
}
}
// _forwardToDelegate property
-(id)_forwardToDelegate {
return __forwardToDelegate;
}
-(void)_setForwardToDelegate:(id __nullable)forwardToDelegate retainDelegate:(BOOL)retainDelegate {
__forwardToDelegate = forwardToDelegate;
if (retainDelegate) {
self.strongForwardDelegate = forwardToDelegate;
}
else {
self.strongForwardDelegate = nil;
}
}
// 是否已经定义了该方法
-(BOOL)hasWiredImplementationForSelector:(SEL)selector {
return [super respondsToSelector:selector];
}
// 查找该selector 是否被包含在voidSelectorsPerClass 内
-(BOOL)voidDelegateMethodsContain:(SEL)selector {
@synchronized(_RXDelegateProxy.class) {
NSSet *voidSelectors = voidSelectorsPerClass[CLASS_VALUE(self.class)];
NSAssert(voidSelectors != nil, @"Set of allowed methods not initialized");
return [voidSelectors containsObject:SEL_VALUE(selector)];
}
}
// 消息转发函数
-(void)forwardInvocation:(NSInvocation *)anInvocation {
BOOL isVoid = RX_is_method_signature_void(anInvocation.methodSignature);
NSArray *arguments = nil;
// 如果函数返回类型,为空 _sentMessage
if (isVoid) {
arguments = RX_extract_arguments(anInvocation);
[self _sentMessage:anInvocation.selector withArguments:arguments];
}
// 如果 _forwardToDelegate 不为空,且响应该方法,那么转发给_forwardToDelegate
if (self._forwardToDelegate && [self._forwardToDelegate respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:self._forwardToDelegate];
}
// 如果函数返回类型,为空 _methodInvoked
if (isVoid) {
[self _methodInvoked:anInvocation.selector withArguments:arguments];
}
}
// abstract method
-(void)_sentMessage:(SEL)selector withArguments:(NSArray *)arguments {
}
// abstract method
-(void)_methodInvoked:(SEL)selector withArguments:(NSArray *)arguments {
}
-(void)dealloc {
}
@end
_RXDelegateProxy 主要做了一下几件事:
- 收集协议里所有返回值为
void
的SEL
- 提供
_forwardToDelegate
用于直接支持代理 - 在合适的时候进行消息转发
好了再来分析一下DelegateProxy
, 先来个前菜:DelegateProxyType
public protocol DelegateProxyType: class {
// 拥有两个范型, 一个是代理人: Delegate,一个是委托人: ParentObject
associatedtype ParentObject: AnyObject
associatedtype Delegate
/// It is require that enumerate call `register` of the extended DelegateProxy subclasses here.
// 注册已知的方法,具体作用后面再分析
static func registerKnownImplementations()
/// Unique identifier for delegate
// Class 唯一标识符 , 重点要关注下这个标识符怎么产生,是用来干嘛的
static var identifier: UnsafeRawPointer { get }
// 设置 currentDelegate property
static func currentDelegate(for object: ParentObject) -> Delegate?
static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject)
// 设置 forwardToDelegate property
func setForwardToDelegate(_ forwardToDelegate: Delegate?, retainDelegate: Bool)
}
看一下官方的注释
+-------------------------------------------+
| |
| 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) |
| |
+-------------------------------------------+
使用 proxy 替代Delegate, proxy 会提供Observable 以供用户订阅, 同时proxy 提供消息转发功能, 用户可以设置 setForwardToDelegate ,兼容原来的Delegate模式
MessageDispatcher
fileprivate final class MessageDispatcher {
private let dispatcher: PublishSubject<[Any]>
private let result: Observable<[Any]>
fileprivate let selector: Selector
init<P, D>(selector: Selector, delegateProxy _delegateProxy: DelegateProxy<P, D>) {
weak var weakDelegateProxy = _delegateProxy
let dispatcher = PublishSubject<[Any]>()
self.dispatcher = dispatcher
self.selector = selector
self.result = dispatcher
.do(onSubscribed: { weakDelegateProxy?.checkSelectorIsObservable(selector); weakDelegateProxy?.reset() }, onDispose: { weakDelegateProxy?.reset() })
.share()
.subscribeOn(mainScheduler)
}
var on: (Event<[Any]>) -> () {
return self.dispatcher.on
}
var hasObservers: Bool {
return self.dispatcher.hasObservers
}
func asObservable() -> Observable<[Any]> {
return self.result
}
}
MessageDispatcher 的功能就在于, 收到相关delegate 相关消息转发的时候, 将这个消息转化为Observable.onNext事件 ,以供用户订阅。
该Observable 是一个共享操作符,(避免do 事件被重复执行),在主线程订阅。
至于do 事件 所做的 checkSelectorIsObservable 和 reset 是干什么待会在研究。
终于轮到 DelegateProxy 出场了, 先看看它有什么属性
open class DelegateProxy<P: AnyObject, D>: _RXDelegateProxy {
public typealias ParentObject = P
public typealias Delegate = D
private var _sentMessageForSelector = [Selector: MessageDispatcher]()
private var _methodInvokedForSelector = [Selector: MessageDispatcher]()
/// Parent object associated with delegate proxy.
private weak private(set) var _parentObject: ParentObject?
fileprivate let _currentDelegateFor: (ParentObject) -> AnyObject?
fileprivate let _setCurrentDelegateTo: (AnyObject?, ParentObject) -> ()
/// Initializes new instance.
///
/// - parameter parentObject: Optional parent object that owns `DelegateProxy` as associated object.
public init<Proxy: DelegateProxyType>(parentObject: ParentObject, delegateProxy: Proxy.Type)
where Proxy: DelegateProxy<ParentObject, Delegate>, Proxy.ParentObject == ParentObject, Proxy.Delegate == Delegate {
self._parentObject = parentObject
self._currentDelegateFor = delegateProxy._currentDelegate
self._setCurrentDelegateTo = delegateProxy._setCurrentDelegate
MainScheduler.ensureExecutingOnScheduler()
#if TRACE_RESOURCES
_ = Resources.incrementTotal()
#endif
super.init()
}
}
两个Dictionary<Selector, MessageDispatcher>, 一个对委托人的弱引用,一个currentDelegate 属性。 并不复杂。继续看看它的方法。
// 这一段利用了懒加载的思想,只在需要的时候创建MessageDispatcher, 避免容器的资源浪费
open func sentMessage(_ selector: Selector) -> Observable<[Any]> {
MainScheduler.ensureExecutingOnScheduler()
let subject = _sentMessageForSelector[selector]
if let subject = subject {
return subject.asObservable()
}
else {
let subject = MessageDispatcher(selector: selector, delegateProxy: self)
_sentMessageForSelector[selector] = subject
return subject.asObservable()
}
}
open func methodInvoked(_ selector: Selector) -> Observable<[Any]> {
MainScheduler.ensureExecutingOnScheduler()
let subject = _methodInvokedForSelector[selector]
if let subject = subject {
return subject.asObservable()
}
else {
let subject = MessageDispatcher(selector: selector, delegateProxy: self)
_methodInvokedForSelector[selector] = subject
return subject.asObservable()
}
}
// 收到消息发送 on 事件
open override func _sentMessage(_ selector: Selector, withArguments arguments: [Any]) {
_sentMessageForSelector[selector]?.on(.next(arguments))
}
// 收到消息发送 on 事件
open override func _methodInvoked(_ selector: Selector, withArguments arguments: [Any]) {
_methodInvokedForSelector[selector]?.on(.next(arguments))
}
这一段是收到消息转发的处理, 收到消息转发后由相应的MessageDispatcher 发出onNext事件。
// 检测是否支持该方法代理
private func hasObservers(selector: Selector) -> Bool {
return (_sentMessageForSelector[selector]?.hasObservers ?? false)
|| (_methodInvokedForSelector[selector]?.hasObservers ?? false)
}
// runtime 核心函数
override open func responds(to aSelector: Selector!) -> Bool {
return super.responds(to: aSelector) // 父类是否响应该方法
|| (self._forwardToDelegate?.responds(to: aSelector) ?? false) // _forwardToDelegate 是否响应该方法
|| (self.voidDelegateMethodsContain(aSelector) && self.hasObservers(selector: aSelector)) // 判断自己是否支持该方法
}
消息转发策略
- 父类响应该方法
- _forwardToDelegate 响应该方法
- 该方法包含在 voidDelegateMethodList 列表类, 且相应的MessageDispather 拥有订阅者
其中第三条策略最为复杂, 因为前两天的返回值是确定的一旦代码编译完结果就确定了,至于第三条有点懵。 试想一下如果 MessageDispather 前一刻拥有Observers,后一刻没有, 那么是不是 前一刻的结果为true,后一刻的结果为false。于是写了个简单Demo验证一下: 传送门
open func forwardToDelegate() -> Delegate? {
return castOptionalOrFatalError(self._forwardToDelegate)
}
/// Sets reference of normal delegate that receives all forwarded messages
/// through `self`.
///
/// - parameter forwardToDelegate: Reference of delegate that receives all messages through `self`.
/// - parameter retainDelegate: Should `self` retain `forwardToDelegate`.
open func setForwardToDelegate(_ delegate: Delegate?, retainDelegate: Bool) {
#if DEBUG // 4.0 all configurations
MainScheduler.ensureExecutingOnScheduler()
#endif
self._setForwardToDelegate(delegate, retainDelegate: retainDelegate)
let sentSelectors: [Selector] = self._sentMessageForSelector.values.filter { $0.hasObservers }.map { $0.selector }
let invokedSelectors: [Selector] = self._methodInvokedForSelector.values.filter { $0.hasObservers }.map { $0.selector }
let allUsedSelectors = sentSelectors + invokedSelectors
for selector in Set(allUsedSelectors) {
checkSelectorIsObservable(selector)
}
self.reset()
}
设置forwardToDelegate 属性, 这里同样有一个问题,在 不同时间段设置forwardToDelegate 属性, 最终可能 allUsedSelectors 的结果不一样。 这个同样需要验证一下。
fileprivate func checkSelectorIsObservable(_ selector: Selector) {
MainScheduler.ensureExecutingOnScheduler()
if hasWiredImplementation(for: selector) {
print("⚠️ Delegate proxy is already implementing `\(selector)`, a more performant way of registering might exist.")
return
}
if voidDelegateMethodsContain(selector) {
return
}
// In case `_forwardToDelegate` is `nil`, it is assumed the check is being done prematurely.
if !(self._forwardToDelegate?.responds(to: selector) ?? true) {
print("⚠️ Using delegate proxy dynamic interception method but the target delegate object doesn't respond to the requested selector. " +
"In case pure Swift delegate proxy is being used please use manual observing method by using`PublishSubject`s. " +
" (selector: `\(selector)`, forwardToDelegate: `\(_forwardToDelegate ?? self)`)")
}
}
checkSelectorIsObservable 分为三个分支:
- 父类已经定义了该方法, 搜索了所有的
hasWiredImplementationForSelector
方法,发现没有被重载,函数实现
-(BOOL)hasWiredImplementationForSelector:(SEL)selector {
return [super respondsToSelector:selector];
}
那么就会给出警告, 指出该方法已经在父类实现, 可能存在更高效实现方法
- 该SEL 返回值为void,合法直接返回
- 该SEL 返回值不为空 ,且在 _forwardToDelegate没有定义,这个时候会给出警告, 但是什么触发这个case 还需要研究一下。
触发该case 传送门: 传送门
// 更新delegate
fileprivate func reset() {
guard let parentObject = self._parentObject else { return }
let maybeCurrentDelegate = _currentDelegateFor(parentObject)
if maybeCurrentDelegate === self {
// 清空当前delegate
_setCurrentDelegateTo(nil, parentObject)
// 重新设置当前delegate
_setCurrentDelegateTo(castOrFatalError(self), parentObject)
}
}
一下情况会触发reset 方法
- setForwardToDelegate
- MessageDispatcher Subscribed
- MessageDispatcher Dispose
网友评论