RxSwift之深入解析特殊序列deallocating与deallocated的源码实现

一、引言

  • 在 RxSwfit 中,有两个特殊序列:deallocating 与 deallocated,deinit 等价于 dealloc。在 deallocating 与 deallocated 两个序列被订阅时,那么当 deinit 调用时会触发这两个序列发送信号,它们的执行顺序为:deallocating -> deinit -> deallocated。
  • 现有如下的测试代码:
	override func viewDidLoad() {
        _ = rx.deallocating.subscribe(onNext: { () in
            print("准备撤退")
        })
        _ = rx.deallocated.subscribe(onNext: { () in
            print("已经撤退")
        })
    }
    
    override func viewDidAppear(_ animated: Bool) {
        print("进来了")
    }
    
    deinit {
        print("\(self.classForCoder) 销毁")
    }
  • 运行如下:
	进来了
	准备撤退
	TestViewController 销毁
	已经撤退
  • 从上面的测试代码可以看出,RxSwift 对 deinit(dealloc)做了处理,通常通过黑魔法就能够达到该效果,在 OC 中,经常使用 runtime 来交换方法,在方法内部处理需要做的事情,那么 RxSwift 是如何实现的呢?

二、deallocating 的源码分析

① deallocating 序列的创建

  • deallocating 是 Reactive 的扩展方法,继承自 AnyObject,相当于 OC 中的 NSObject,如下所示:
	extension Reactive where Base: AnyObject {
	    public var deallocating: Observable<()> {
	        return self.synchronized {
	            do {
	                let proxy: DeallocatingProxy = try self.registerMessageInterceptor(deallocSelector)
	                return proxy.messageSent.asObservable()
	            }
	            catch let e {
	                return Observable.error(e)
	            }
	        }
	    }
	}
  • 源码分析:
    • 使用同步锁来保证线程安全;
    • 内部通过 self.registerMessageInterceptor 传入 deallocSelector 来初始化一个 DeallocatingProxy 对象;
    • 通过 messageSent 获取一个 ReplaySubject 序列。
  • deallocSelector,顾名思义,明显是一个方法选择器,它的实现是使用 NSSelectorFromString 方法来获取 dealloc 选择器,如下所示:
	private let deallocSelector = NSSelectorFromString("dealloc")
  • 由此可以看出,RxSwift 确实是在 dealloc(即 Swfit 中的 deinit)上做了处理,这里只是初始化了 proxy 对象,具体消息如何传出来的,继续往下探究。

② proxy 对象的创建

  • 继续查看 registerMessageInterceptor 方法:
	fileprivate func registerMessageInterceptor<T: MessageInterceptorSubject>(_ selector: Selector) throws -> T {
	    let rxSelector = RX_selector(selector)
	    let selectorReference = RX_reference_from_selector(rxSelector)
	
	    let subject: T
	    if let existingSubject = objc_getAssociatedObject(self.base, selectorReference) as? T {
	        subject = existingSubject
	    }
	    else {
	        subject = T()
	        objc_setAssociatedObject(
	            self.base,
	            selectorReference,
	            subject,
	            .OBJC_ASSOCIATION_RETAIN_NONATOMIC
	        )
	    }
	
	    if subject.isActive {
	        return subject
	    }
	
	    var error: NSError?
	    let targetImplementation = RX_ensure_observing(self.base, selector, &error)
	    if targetImplementation == nil {
	        throw error?.rxCocoaErrorForTarget(self.base) ?? RxCocoaError.unknown
	    }
	
	    subject.targetImplementation = targetImplementation!
	    return subject
	}
  • 源码分析:
    • selector 外部传入的 dealloc 的方法选择器;
    • RX_selector 方法通过 dealloc 方法名构建了另外一个方法选择器,如下:
	SEL __nonnull RX_selector(SEL __nonnull selector) {
	    NSString *selectorString = NSStringFromSelector(selector);
	    return NSSelectorFromString([RX_PREFIX stringByAppendingString:selectorString]);
	}
  • 由上可以看出,代码进入到 OC 区了,使用了 OC 的方法来满足需求。沿着想要的结果去找方法,前面提到 dealloc 可能被替换了,通过代码中的 targetImplementation,貌似是一个目标实现,进入代码查看:
	IMP __nullable RX_ensure_observing(id __nonnull target, SEL __nonnull selector, NSErrorParam error) {
	    __block IMP targetImplementation = nil;
	    @synchronized(target) {
	        @synchronized([target class]) {
	            [[RXObjCRuntime instance] performLocked:^(RXObjCRuntime * __nonnull self) {
	                targetImplementation = [self ensurePrepared:target
	                                               forObserving:selector
	                                                      error:error];
	            }];
	        }
	    }
	    return targetImplementation;
	}
  • 源码分析:
    • RX_ensure_observing 返回一个 IMP 函数指针;
    • [RXObjCRuntime instance] 实际上是一个 NSObject 的一个单例,内部采用互斥锁,向外部提供当前单例对象;
    • ensurePrepared 消息发送的入口点。

③ ensurePrepared 函数

  • 直接搜索 ensurePrepared 方法:
	-(IMP __nullable)ensurePrepared:(id __nonnull)target forObserving:(SEL __nonnull)selector error:(NSErrorParam)error {
	    Method instanceMethod = class_getInstanceMethod([target class], selector);
	    if (instanceMethod == nil) {
	        RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
	                                           code:RXObjCRuntimeErrorSelectorNotImplemented
	                                       userInfo:nil], nil);
	    }
	
	    if (selector == @selector(class)
	    ||  selector == @selector(forwardingTargetForSelector:)
	    ||  selector == @selector(methodSignatureForSelector:)
	    ||  selector == @selector(respondsToSelector:)) {
	        RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomain
	                                           code:RXObjCRuntimeErrorObservingPerformanceSensitiveMessages
	                                       userInfo:nil], nil);
	    }
	
	    // For `dealloc` message, original implementation will be swizzled.
	    // This is a special case because observing `dealloc` message is performed when `observeWeakly` is used.
	    //
	    // Some toll free bridged classes don't handle `object_setClass` well and cause crashes.
	    //
	    // To make `deallocating` as robust as possible, original implementation will be replaced.
	    if (selector == deallocSelector) {
	        Class __nonnull deallocSwizzingTarget = [target class];
	        IMP interceptorIMPForSelector = [self interceptorImplementationForSelector:selector forClass:deallocSwizzingTarget];
	        if (interceptorIMPForSelector != nil) {
	            return interceptorIMPForSelector;
	        }
	
	        if (![self swizzleDeallocating:deallocSwizzingTarget error:error]) {
	            return nil;
	        }
	
	        interceptorIMPForSelector = [self interceptorImplementationForSelector:selector forClass:deallocSwizzingTarget];
	        if (interceptorIMPForSelector != nil) {
	            return interceptorIMPForSelector;
	        }
	    }
	}
  • 源码分析:
    • class_getInstanceMethod 获取当前界面对象的 dealloc 方法,来判断该类是否存在该方法,容错处理,对方法替换没关系;
    • 注释中说明了替换原始的 dealloc 方法;
    • deallocSwizzingTarget 获取到要替换 dealloc 的目标类;
    • swizzleDeallocating 传入目标类准备替换 dealloc 为 deallocating。

④ swizzleDeallocating

  • 可以看到,有一个如下的函数宏定义:
	SWIZZLE_INFRASTRUCTURE_METHOD(
	    void,
	    swizzleDeallocating,
	    ,
	    deallocSelector,
	    DEALLOCATING_BODY
	)
  • 内部整理如下:
	#define SWIZZLE_INFRASTRUCTURE_METHOD(return_value, method_name, parameters, method_selector, body, ...)
	SWIZZLE_METHOD(return_value, -(BOOL)method_name:(Class __nonnull)class parameters error:(NSErrorParam)error
	{
	    SEL selector = method_selector; , body, NO_BODY, __VA_ARGS__)
	    
	    
	    // common base
	    
	#define SWIZZLE_METHOD(return_value, method_prototype, body, invoked_body, ...)
	    method_prototype
	    __unused SEL rxSelector = RX_selector(selector);
	    IMP (^newImplementationGenerator)(void) = ^() {
	        __block IMP thisIMP = nil;
	        id newImplementation = ^return_value(__unsafe_unretained id self DECLARE_ARGUMENTS(__VA_ARGS__)) {
	            body(__VA_ARGS__)
	            
	            struct objc_super superInfo = {
	                .receiver = self,
	                .super_class = class_getSuperclass(class)
	            };
	            
	            return_value (*msgSend)(struct objc_super *, SEL DECLARE_ARGUMENTS(__VA_ARGS__))
	            = (__typeof__(msgSend))objc_msgSendSuper;
	            @try {
	                return msgSend(&superInfo, selector ARGUMENTS(__VA_ARGS__));
	            }
	            @finally { invoked_body(__VA_ARGS__) }
	        };
	        
	        thisIMP = imp_implementationWithBlock(newImplementation);
	        return thisIMP;
	    };
	    
	    IMP (^replacementImplementationGenerator)(IMP) = ^(IMP originalImplementation) {
	        __block return_value (*originalImplementationTyped)(__unsafe_unretained id, SEL DECLARE_ARGUMENTS(__VA_ARGS__) )
	        = (__typeof__(originalImplementationTyped))(originalImplementation);
	        
	        __block IMP thisIMP = nil;
	        id implementationReplacement = ^return_value(__unsafe_unretained id self DECLARE_ARGUMENTS(__VA_ARGS__) ) {
	            body(__VA_ARGS__)
	            @try {
	                return originalImplementationTyped(self, selector ARGUMENTS(__VA_ARGS__));
	            }
	            @finally { invoked_body(__VA_ARGS__) }
	        };
	        
	        thisIMP = imp_implementationWithBlock(implementationReplacement);
	        return thisIMP;
	    };
	    
	    return [self ensureSwizzledSelector:selector
	                                ofClass:class
	             newImplementationGenerator:newImplementationGenerator
	     replacementImplementationGenerator:replacementImplementationGenerator
	                                  error:error];
	}
  • 代码看上去比较繁琐,可以将参数一一对比,能够看到:内部实际是重新组合了一个方法,参数为当前界面对象的类 deallocSwizzingTarget,实现了一个闭包并返回 IMP 函数指针:
    • replacementImplementationGenerator 代码块保存原始 dealloc 的函数地址,并在内部调用;
    • 在代码块中调用了 imp_implementationWithBlock 函数,获取代码块的函数指针。

⑤ imp_implementationWithBlock

  • 该函数接收一个 block 将其拷贝到堆区,返回一个 IMP 函数指针,把 block 当做 OC 中类的方法实现来使用。如下所示,用 block 代替原有方法实现:
	- (void)myMethod {
	    NSLog(@"来了");
	}
	……
	// 创建block
	void (^myblock)(int val) = ^(int val){
	    NSLog(@"myblock");
	};
	// 获取block的IMP
	IMP myblockImp = imp_implementationWithBlock(myblock);
	// 获取要替换的方法的IMP
	Method method = class_getInstanceMethod(self.class, @selector(myMethod));
	// 替换函数指针,指向block
	method_setImplementation(method, myblockImp);
	// 执行原始方法
	[self myMethod];
  • 使用该函数是为了用代码块来替换一个需要替换的方法。以上宏定义的函数最后调用了 ensureSwizzledSelector 方法。

⑥ ensureSwizzledSelector

  • 搜索 ensureSwizzledSelector,查看代码:
	- (BOOL)ensureSwizzledSelector:(SEL __nonnull)selector
	                      ofClass:(Class __nonnull)class
	   newImplementationGenerator:(IMP(^)(void))newImplementationGenerator
	replacementImplementationGenerator:(IMP (^)(IMP originalImplementation))replacementImplementationGenerator
	                        error:(NSErrorParam)error {
	    if ([self interceptorImplementationForSelector:selector forClass:class] != nil) {
	        DLOG(@"Trying to register same intercept at least once, this sounds like a possible bug");
	        return YES;
	    }
	
	#if TRACE_RESOURCES
	    atomic_fetch_add(&numberOInterceptedMethods, 1);
	#endif
	    
	    DLOG(@"Rx is swizzling `%@` for `%@`", NSStringFromSelector(selector), class);
	
	    Method existingMethod = class_getInstanceMethod(class, selector);
	    ALWAYS(existingMethod != nil, @"Method doesn't exist");
	
	    const char *encoding = method_getTypeEncoding(existingMethod);
	    ALWAYS(encoding != nil, @"Encoding is nil");
	
	    IMP newImplementation = newImplementationGenerator();
	
	    if (class_addMethod(class, selector, newImplementation, encoding)) {
	        // new method added, job done
	        [self registerInterceptedSelector:selector implementation:newImplementation forClass:class];
	
	        return YES;
	    }
	
	    imp_removeBlock(newImplementation);
	
	    // if add fails, that means that method already exists on targetClass
	    Method existingMethodOnTargetClass = existingMethod;
	
	    IMP originalImplementation = method_getImplementation(existingMethodOnTargetClass);
	    ALWAYS(originalImplementation != nil, @"Method must exist.");
	    IMP implementationReplacementIMP = replacementImplementationGenerator(originalImplementation);
	    ALWAYS(implementationReplacementIMP != nil, @"Method must exist.");
	    IMP originalImplementationAfterChange = method_setImplementation(existingMethodOnTargetClass, implementationReplacementIMP);
	    ALWAYS(originalImplementation != nil, @"Method must exist.");
	
	    // If method replacing failed, who knows what happened, better not trying again, otherwise program can get
	    // corrupted.
	    [self registerInterceptedSelector:selector implementation:implementationReplacementIMP forClass:class];
	
	    if (originalImplementationAfterChange != originalImplementation) {
	        THREADING_HAZARD(class);
	        return NO;
	    }
	
	    return YES;
	}
  • 源码分析:
    • interceptorImplementationForSelector 查看 dealloc 是否存在对应的函数,如果继续往下执行,则开始对 dealloc 做替换;
    • class_addMethod,既然 dealloc 存在对应的函数,添加必然失败,继续向下执行;
    • method_setImplementation,开始设置 dealloc 的 IMP 指向上面提到的代码块 replacementImplementationGenerator 中。
  • 在此处即替换了系统方法,当系统调用了 dealloc 时就会触发 replacementImplementationGenerator 中的 block 方法:
	IMP (^replacementImplementationGenerator)(IMP) = ^(IMP originalImplementation) {
	    __block return_value (*originalImplementationTyped)(__unsafe_unretained id, SEL DECLARE_ARGUMENTS(__VA_ARGS__) )
	    = (__typeof__(originalImplementationTyped))(originalImplementation);
	    
	    __block IMP thisIMP = nil;
	    id implementationReplacement = ^return_value(__unsafe_unretained id self DECLARE_ARGUMENTS(__VA_ARGS__) ) {
	        body(__VA_ARGS__)
	        @try {
	            return originalImplementationTyped(self, selector ARGUMENTS(__VA_ARGS__));
	        }
	        @finally { invoked_body(__VA_ARGS__) }
	    };
	    
	    thisIMP = imp_implementationWithBlock(implementationReplacement);
	    return thisIMP;
	};
  • 可以看到存在一个 body 函数的调用,body 做了什么呢?

⑦ body-DEALLOCATING_BODY

  • 搜索找到宏 DEALLOCATING_BODY,整理如下:
	#define DEALLOCATING_BODY(...)
	id<RXDeallocatingObserver> observer = objc_getAssociatedObject(self, rxSelector);
	if (observer != nil && observer.targetImplementation == thisIMP) {
	    [observer deallocating];
	}
  • 源码分析:
    • rxSelector 即是要替换的方法选择器即 deallocating 对应的选择器;
    • observer 序列在此处调用了 deallocating,此时 deallocating 就被调用,commpleted 结束序列,因此不需要在外部添加垃圾袋,如下:
	@objc func deallocating() {
	    self.messageSent.on(.next(()))
	}
	deinit {
	    self.messageSent.on(.completed)
	}
  • 此处即是向订阅发送消息,deallocating 调用后,body 调用后即调用代码块保存的原始 dealloc 函数:
	return originalImplementationTyped(self, selector ARGUMENTS(__VA_ARGS__));
  • 由上可知,originalImplementationTyped 是 dealloc 的原始函数,在此处调用了 dealloc。如果需要验证该处就是触发的 dealloc 方法,可以将次闭包的参数换成 viewDidAppear,在 RxCocoa -> _RXObjeCRuntime.m 中的 ensureSwizzledSelector 方法中替换:
	replacementImplementationGenerator(originalImplementation);

	// 替换为
	IMP viewdidAppear = class_getMethodImplementation(class, @selector(viewDidAppear:));
    IMP implementationReplacementIMP = replacementImplementationGenerator(viewdidAppear);
  • 替换为视图出现时调用的方法,如果在掉用 deallocating 后,viewdidAppear 被调用就能够验证上面所指之处就是触发的 dealloc 方法。
  • 上面的测试代码:
    • 替换前的运行结果:
	进来了
	准备撤退
	TestViewController 销毁
	已经撤退
    • 替换后的运行结果则为:
	进来了
	准备撤退
	进来了
  • 通过以上测试,能够确定 dealloc 是在代码块中调用的(注意:在修改源码后要 clean 一下工程,否则缓存会影响执行结果)。

三、deallocated 的源码分析

  • 继续看看 deallocated 序列是如何产生,又是如何在 dealloc 调用完成之后执行的:
	public var deallocated: Observable<Void> {
	    return self.synchronized {
	        if let deallocObservable = objc_getAssociatedObject(self.base, &deallocatedSubjectContext) as? DeallocObservable {
	            return deallocObservable._subject
	        }
	
	        let deallocObservable = DeallocObservable()
	
	        objc_setAssociatedObject(self.base, &deallocatedSubjectContext, deallocObservable, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
	        return deallocObservable._subject
	    }
	}
  • 关联了创建的序列,保证当前控制器内的序列对象只有一个。
  • DeallocObservable 的源码如下:
fileprivate final class DeallocObservable {
    let _subject = ReplaySubject<Void>.create(bufferSize:1)

    init() {
    }

    deinit {
        self._subject.on(.next(()))
        self._subject.on(.completed)
    }
}
  • 源码分析:
    • 内部也初始化一个 ReplaySubject 序列,用来发送消息;
    • 在对象销毁时调用了 .next 和 .completed,发送一条消息,再发送一条完成消息终止序列,因此在外部创建序列不需要添加垃圾袋。

四、总结

  • 在 RxSwift 中提供了两个关于 dealloc(deinit)的序列,观察 dealloc 的调用,其中 deallocating 内部替换了原生的 dealloc 方法从而达到监听 dealloc 的调用;
  • deallocating 并不是交换方法,而是在 replacementImplementationGenerator 代码块中先保留了 dealloc 的函数地址,再通过 imp_implementationWithBlock 设置 dealloc 的 IMP,指向了 replacementImplementationGenerator 代码块;
  • 调用 dealloc 方法就会调用了代码块,在代码块内部通过 body 函数调用了 deallocating 方法,之后执行代码块中保留的原 dealloc 函数。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

╰つ栺尖篴夢ゞ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值