日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2024/5/21 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 RxSwift之深入解析特殊序列deallocating与deallocated的源码实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、引言

  • 在 RxSwfit 中,有兩個特殊序列:deallocating 與 deallocated,deinit 等價于 dealloc。在 deallocating 與 deallocated 兩個序列被訂閱時,那么當 deinit 調用時會觸發這兩個序列發送信號,它們的執行順序為:deallocating -> deinit -> deallocated。
  • 現有如下的測試代碼:
override func viewDidLoad() {_ = rx.deallocating.subscribe(onNext: { () inprint("準備撤退")})_ = rx.deallocated.subscribe(onNext: { () inprint("已經撤退")})}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: Tif 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:targetforObserving:selectorerror: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:RXObjCRuntimeErrorDomaincode:RXObjCRuntimeErrorSelectorNotImplementeduserInfo:nil], nil);}if (selector == @selector(class)|| selector == @selector(forwardingTargetForSelector:)|| selector == @selector(methodSignatureForSelector:)|| selector == @selector(respondsToSelector:)) {RX_THROW_ERROR([NSError errorWithDomain:RXObjCRuntimeErrorDomaincode:RXObjCRuntimeErrorObservingPerformanceSensitiveMessagesuserInfo: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:selectorofClass:classnewImplementationGenerator:newImplementationGeneratorreplacementImplementationGenerator:replacementImplementationGeneratorerror:error];}
  • 代碼看上去比較繁瑣,可以將參數一一對比,能夠看到:內部實際是重新組合了一個方法,參數為當前界面對象的類 deallocSwizzingTarget,實現了一個閉包并返回 IMP 函數指針:
    • replacementImplementationGenerator 代碼塊保存原始 dealloc 的函數地址,并在內部調用;
    • 在代碼塊中調用了 imp_implementationWithBlock 函數,獲取代碼塊的函數指針。

⑤ imp_implementationWithBlock

  • 該函數接收一個 block 將其拷貝到堆區,返回一個 IMP 函數指針,把 block 當做 OC 中類的方法實現來使用。如下所示,用 block 代替原有方法實現:
- (void)myMethod {NSLog(@"來了");}……// 創建blockvoid (^myblock)(int val) = ^(int val){NSLog(@"myblock");};// 獲取block的IMPIMP myblockImp = imp_implementationWithBlock(myblock);// 獲取要替換的方法的IMPMethod method = class_getInstanceMethod(self.class, @selector(myMethod));// 替換函數指針,指向blockmethod_setImplementation(method, myblockImp);// 執行原始方法[self myMethod];
  • 使用該函數是為了用代碼塊來替換一個需要替換的方法。以上宏定義的函數最后調用了 ensureSwizzledSelector 方法。

⑥ ensureSwizzledSelector

  • 搜索 ensureSwizzledSelector,查看代碼:
- (BOOL)ensureSwizzledSelector:(SEL __nonnull)selectorofClass:(Class __nonnull)classnewImplementationGenerator:(IMP(^)(void))newImplementationGeneratorreplacementImplementationGenerator:(IMP (^)(IMP originalImplementation))replacementImplementationGeneratorerror:(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_RESOURCESatomic_fetch_add(&numberOInterceptedMethods, 1);#endifDLOG(@"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 targetClassMethod 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 函數。

總結

以上是生活随笔為你收集整理的RxSwift之深入解析特殊序列deallocating与deallocated的源码实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。