RxCocoa中的AOP实现

原文出处

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

介绍

在RxCocoa的代码中,对于NSObject的扩展中,有这两个代码,实现了对于实例方法的AOP

public func sentMessage(_ selector: Selector) -> Observable<[Any]>
public func methodInvoked(_ selector: Selector) -> Observable<[Any]>

在本质上说,他们对selector的触发时机进行监听,并分别在selector触发前、触发后插入自定义的方法。

下面是具体的使用场景:

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {
    private let bag = DisposeBag()

    override func viewDidLoad() {
        self.rx.sentMessage(#selector(ViewController.viewWillAppear(_:))).asObservable().subscribe { (_) in
            print("sendMessage")
        }.disposed(by: bag)
        
        self.rx.methodInvoked(#selector(ViewController.viewWillAppear(_:))).asObservable().subscribe { (_) in
            print("methodInvoked")
        }.disposed(by: bag)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("viewWillAppear")
    }
}

输出结果


输出结果

最终的效果是在该方法的执行前与执行后的两个时间点插入了其他的方法。

下面我们就剖析一下,RxCocoa究竟做了些什么。
本文会从外部调用的方法setMessage开始,一步步查看内部的具体的实现。

Let's go

简单剖析一下上面的代码,它们分别是对于两个Observable进行了监听(RxSwift的介绍)。换句话说,在实际的实现过程中,
selector对应的方法在即将执行前肯定是要触发setMessage所对应的Observable并发出信号。
selector对应的方法在执行完成肯定要触发methodInvoked所对应的Observable并发出信号。

当然,这都是我们基于当前的调用方法给出的推测,那看看实际都做了些什么。

首先看setMessage所对应的方法的具体实现。

   public func sentMessage(_ selector: Selector) -> Observable<[Any]> {
        return synchronized {
            // in case of dealloc selector replay subject behavior needs to be used
            if selector == deallocSelector {
                return deallocating.map { _ in [] }
            }

            do {
                let proxy: MessageSentProxy = try registerMessageInterceptor(selector) 
                return proxy.messageSent.asObservable()
            }
            catch let e {
                return Observable.error(e)
            }
        }
    }

上面的代码有2行是核心。

let proxy: MessageSentProxy = try registerMessageInterceptor(selector)
proxy.messageSent.asObservable()

其中第一行代码实现的功能是: 为该selector创建或者获取一个代理对象
第二行代码,则是返回messageSent的信号源,它能保证在恰当的时候出发信号。

下面分别要对这两个代码进行具体的分析

MessageSentProxy

贴一下MessageSentProxy的具体的代码

fileprivate final class MessageSentProxy
        : MessageInterceptorSubject
        , RXMessageSentObserver {
        typealias E = [AnyObject]

        let messageSent = PublishSubject<[Any]>() //message所对应的subject(Observable),也就是被观察者
        let methodInvoked = PublishSubject<[Any]>()//invoked 所对应的被观察者

        @objc var targetImplementation: IMP = RX_default_target_implementation()

        var isActive: Bool {
            return targetImplementation != RX_default_target_implementation()
        }

        init() {
        }

        @objc func messageSent(withArguments arguments: [Any]) -> Void {
            messageSent.on(.next(arguments))
        }

        @objc func methodInvoked(withArguments arguments: [Any]) -> Void {
            methodInvoked.on(.next(arguments))
        }

        deinit {
            messageSent.on(.completed)
            methodInvoked.on(.completed)
        }
    }

从上面可以看出,MessageSentProxy这个类分别持有了名为messageSent以及methodInvoked的两个被观察者。
应该可以看出,他们分别对应着selector前后的触发时机,对这两个参数进行监听,也就是变相的监听了selector触发的前后的时机。

其中内部的messageSent(withArguments arguments: [Any])以及methodInvoked(withArguments arguments: [Any])方法分别促使两个被观察者发出信号。

两个方法分别用@objc进行修饰,标明两个方法都可以被OC的调用机制触发。

那当前可以整理出的触发顺序是:

  1. MessageSentProxy中messageSent(withArguments arguments: [Any])发放被调用
  2. MessageSentProxy中messageSent = PublishSubject<[Any]>()触发onNext信号
  3. 外部对于messageSent的监听事件触发

那好,继续深究一下。

registerMessageInterceptor(selector)

上面讲到registerMessageInterceptor(selector)方法是通过selector来创建一个MessageSentProxy,用来实现Observable。 由于同一个selector可能有2个监听事件sentMessage以及methodInvoke, 而两者都是通过同一个MessageSentProxy来实现。因此最终一个selector对应于一个MessageSentProxy,也不需要进行反复的创建。
那究竟做了哪些事情呢?

 //该方法保证了,在同一个target上 同一个selector 只会有 一个MessageInterceptorSubject与之绑定。
    fileprivate func registerMessageInterceptor(_ selector: Selector) throws -> T {
        let rxSelector = RX_selector(selector) //添加前缀,生成一个新的selector 当前只有名字
        let selectorReference = RX_reference_from_selector(rxSelector) 

        let subject: T
        if let existingSubject = objc_getAssociatedObject(base, selectorReference) as? T { //如果这个selector有关联值的话, 赋值给subject
            subject = existingSubject
        }
        else { //如果没有,则创建一个Subject对象,把它关联给当前的base
            subject = T()
            objc_setAssociatedObject(
                base,
                selectorReference,
                subject,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC
            )
        }

        if subject.isActive { // 表示其内部的IMP 其实已经赋值了, 否则就是还没有赋值。
            return subject
        }

        var error: NSError?
        let targetImplementation = RX_ensure_observing(base, selector, &error)
        if targetImplementation == nil {
            throw error?.rxCocoaErrorForTarget(base) ?? RxCocoaError.unknown
        }

        subject.targetImplementation = targetImplementation!

        return subject

上面的代码做了大概几件事:
1.将MessageInterceptorSubject,也就是该方法调用处的MessageSentProxy通过selector与当前的对象进行绑定。

2.如果对应的selector已经绑定过了,则获取已绑定的MessageSentProxy。
这是由于对应同一个selector,我们可能有sentMessage以及methodInvoke两个监听事件,两个监听事件由于可以使用同一个MessageSentProxy,避免重复工作。
3.开始hook对应的selector,进行插入操作
核心代码
let targetImplementation = RX_ensure_observing(base, selector, &error)

解读方法的hook

这也是当前这个过程中最核心的部分。
还记得KVO是怎么实现的吗?如果不记得,可以在看一下(KVO代码测试以及探究)

继续向下深究RX_ensure_observing(base, selector, &error),并且删除用于行,留下核心代码

/**
 This is the main entry point for observing messages sent to arbitrary objects.
 */
-(IMP __nullable)ensurePrepared:(id __nonnull)target forObserving:(SEL __nonnull)selector error:(NSErrorParam)error {
    Method instanceMethod = class_getInstanceMethod([target class], selector); //根据target sel 获取method

        Class __nullable swizzlingImplementorClass = [self prepareTargetClassForObserving:target error:error];
        if (swizzlingImplementorClass == nil) {
            return nil;
        }

        NSString *methodEncoding = RX_method_encoding(instanceMethod);
        RXInterceptWithOptimizedObserver optimizedIntercept = optimizedObserversByMethodEncoding[methodEncoding];

        if (!RX_method_has_supported_return_type(instanceMethod)) {
            RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
                                               code:RXObjCRuntimeErrorObservingMessagesWithUnsupportedReturnType
                                           userInfo:nil], nil);
        }

        // optimized interception method
        if (optimizedIntercept != nil) {
            IMP interceptorIMPForSelector = [self interceptorImplementationForSelector:selector forClass:swizzlingImplementorClass];
            if (interceptorIMPForSelector != nil) {
                return interceptorIMPForSelector;
            }

            if (!optimizedIntercept(self, swizzlingImplementorClass, selector, error)) {
                return nil;
            }

            interceptorIMPForSelector = [self interceptorImplementationForSelector:selector forClass:swizzlingImplementorClass];
            if (interceptorIMPForSelector != nil) {
                return interceptorIMPForSelector;
            }
        }
        // default fallback to observing by forwarding messages
        else {
            if ([self forwardingSelector:selector forClass:swizzlingImplementorClass]) {
                return RX_default_target_implementation();
            }

            if (![self observeByForwardingMessages:swizzlingImplementorClass
                                          selector:selector
                                            target:target
                                             error:error]) {
                return nil;
            }

            if ([self forwardingSelector:selector forClass:swizzlingImplementorClass]) {
                return RX_default_target_implementation();
            }
        }
    

    RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
                                       code:RXObjCRuntimeErrorUnknown
                                   userInfo:nil], nil);
}

那实际是如何fake出一个新的派生的class来取代原来的class呢?

-(Class __nullable)prepareTargetClassForObserving:(id __nonnull)target error:(NSErrorParam)error

这个方法做了一下几件事
1.如果已经有了, 则直接返回(通过关联值进行关联)
2.如果class方法与object_getClass获取的元类型不一样,判断是否为KVO,若不是,默认无法处理,返回错误
3.生成最终的fakeClass, 并与关联类型进行绑定。

其中,这个fakeClass就类似于KVO机制中的NSKVONotifying_类名. 都是起到一个替换的作用,让实际方法执行的selector被重写,执行的起始以及结尾分别加入触发。

那说到底,还是没有说,具体这两个方法在细节上是如何插入对应的节点的(捂脸)。

有关于fakeClass的具体实现过程,我会在下一篇文章中进行详细的描述。

你可能感兴趣的:(RxCocoa中的AOP实现)