
在RxSwfit中,有兩個特殊序列
deallocating序列deallocated序列
在RxSwift中deinit等價于dealloc,在上面兩個序列被訂閱時,那么當(dāng)deinit調(diào)用時會觸發(fā)上面兩個序列發(fā)送信號。執(zhí)行順序:deallocating -> deinit -> deallocated。看一段代碼:
override func viewDidLoad() {
_ = rx.deallocating.subscribe(onNext: { () in
print("準(zhǔn)備走了")
})
_ = rx.deallocated.subscribe(onNext: { () in
print("已經(jīng)走了")
})
}
override func viewDidAppear(_ animated: Bool) {
print("我來了")
}
deinit {
print("\(self.classForCoder) 銷毀")
}
打印如下:
我來了
準(zhǔn)備走了
SecondController 銷毀
已經(jīng)走了
從上面代碼我們可以看出,RxSwift對deinit(dealloc)動了手腳,通常通過黑魔法就能夠達(dá)到該效果,在OC中我們經(jīng)常使用runtime來交換方法,在方法內(nèi)部處理我們需要做的事情。那么RxSwift是如何實(shí)現(xiàn)的呢?下面就看看源碼都做了哪些事情。
deallocating序列的創(chuàng)建
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)
}
}
}
}
-
deallocating是Reactive的擴(kuò)展方法,繼承自AnyObject相當(dāng)于OC中的NSObject - 使用同步鎖來保證線程安全
- 內(nèi)部通過
self.registerMessageInterceptor傳入deallocSelector來初始化一個DeallocatingProxy對象 - 通過
messageSent獲取一個ReplaySubject序列
deallocSelector一看就是一個方法選擇器。實(shí)現(xiàn)如下:
private let deallocSelector = NSSelectorFromString("dealloc")
- 使用
NSSelectorFromString方法來獲取dealloc選擇器
由此可以看出,RxSwift確實(shí)是在dealloc(即Swfit中的deinit)上做文章。這里只是初始化了proxy對象,具體消息如何傳出來的,還要繼續(xù)代碼追蹤。
proxy對象的創(chuàng)建
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方法名構(gòu)建了另外一個方法選擇器
SEL __nonnull RX_selector(SEL __nonnull selector) {
NSString *selectorString = NSStringFromSelector(selector);
return NSSelectorFromString([RX_PREFIX stringByAppendingString:selectorString]);
}
從上面以看出我們的代碼進(jìn)入到OC區(qū)了,使用OC的方法來滿足需求。沿著我們想要的結(jié)果去找方法,前面提到dealloc可能被替換了,通過代碼中的targetImplementation,感覺像是一個目標(biāo)實(shí)現(xiàn),進(jìn)入代碼看一下:
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;
}
- 返回一個
IMP函數(shù)指針 -
[RXObjCRuntime instance]實(shí)際上是一個NSObject的一個單例,內(nèi)部采用互斥鎖,向外部提供當(dāng)前單例對象 -
ensurePrepared消息發(fā)送的入口點(diǎn)
ensurePrepared函數(shù)
搜索或直接cmd+點(diǎn)擊定位代碼:
-(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獲取當(dāng)前界面對象的dealloc方法,來判斷該類是否存在該方法,容錯處理,對方法替換沒關(guān)系 - 再看看注釋:替換原始的
dealloc方法。好像是我們需要找的地方 -
deallocSwizzingTarget獲取到要替換dealloc的目標(biāo)類 -
swizzleDeallocating傳入目標(biāo)類準(zhǔn)備替換dealloc為deallocating
swizzleDeallocating
SWIZZLE_INFRASTRUCTURE_METHOD(
void,
swizzleDeallocating,
,
deallocSelector,
DEALLOCATING_BODY
)
該處是個函數(shù)宏定義,內(nèi)部整理如下:
#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];
}
代碼看上去很繁瑣,將參數(shù)一一對比能夠看到,內(nèi)部實(shí)際是重新組合了一個方法,參數(shù)為當(dāng)前界面對象的類deallocSwizzingTarget。內(nèi)部實(shí)現(xiàn)了一個閉包并返回IMP函數(shù)指針:
-
replacementImplementationGenerator代碼塊保存原始dealloc的函數(shù)地址,并在內(nèi)部調(diào)用 - 在代碼塊中調(diào)用了
imp_implementationWithBlock函數(shù),獲取代碼塊的函數(shù)指針
下面先看一下imp_implementationWithBlock函數(shù)的作用。
imp_implementationWithBlock
該函數(shù)接收一個block將其拷貝到堆區(qū),返回一個IMP函數(shù)指針,把block當(dāng)做OC中類的方法實(shí)現(xiàn)來使用。舉例如下,用block代替原有方法實(shí)現(xiàn):
-(void)myMethod{
NSLog(@"我來了");
}
……
//1、創(chuàng)建block
void (^myblock)(int val) = ^(int val){
NSLog(@"myblock");
};
//2、獲取block的IMP
IMP myblockImp = imp_implementationWithBlock(myblock);
//3、獲取要替換的方法的IMP
Method method = class_getInstanceMethod(self.class, @selector(myMethod));
//4、替換函數(shù)指針,指向block
method_setImplementation(method, myblockImp);
//5、執(zhí)行原始方法
[self myMethod];
打?。何襾砹?/p>
使用該函數(shù)是為了用代碼塊來替換一個需要替換的方法。
以上宏定義的函數(shù)最后調(diào)用了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是否存在對應(yīng)的函數(shù),如果有往下走,開始對dealloc做替換 -
class_addMethod,既然dealloc存在對應(yīng)的函數(shù),添加必然失敗,繼續(xù)向下走 -
method_setImplementation,開始設(shè)置dealloc的IMP指向上面提到的代碼塊replacementImplementationGenerator中
在此處即替換了系統(tǒng)方法,當(dāng)系統(tǒng)調(diào)用了dealloc時就會觸發(fā)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函數(shù)的調(diào)用,該處即是關(guān)鍵。
body-DEALLOCATING_BODY
搜索找到宏并整理如下:
#define DEALLOCATING_BODY(...)
id<RXDeallocatingObserver> observer = objc_getAssociatedObject(self, rxSelector);
if (observer != nil && observer.targetImplementation == thisIMP) {
[observer deallocating];
}
-
rxSelector即是要替換的方法選擇器即deallocating對應(yīng)的選擇器 -
observer序列在此處調(diào)用了deallocating,此時deallocating就被調(diào)用
@objc func deallocating() {
self.messageSent.on(.next(()))
}
deinit {
self.messageSent.on(.completed)
}
-
.commpleted結(jié)束序列,因此不需要在外部添加垃圾袋
此處即是向訂閱發(fā)送消息,這里前邊文章都有代碼追蹤這里就不一一介紹了。deallocating調(diào)用后,上面有講到,body調(diào)用后即調(diào)用代碼塊保存的原始dealloc函數(shù):
return originalImplementationTyped(self, selector ARGUMENTS(__VA_ARGS__));
聯(lián)系上面定義,可知originalImplementationTyped是dealloc的原始函數(shù),在此處調(diào)用了dealloc,由于代碼比較繁瑣,下面來證明一下該處就是觸發(fā)dealloc的方法。我們可以將次閉包的參數(shù)換成viewDidAppear,在RxCocoa -> _RXObjeCRuntime.m中的ensureSwizzledSelector方法中替換:
將如下:
replacementImplementationGenerator(originalImplementation);
替換為:
IMP viewdidAppear = class_getMethodImplementation(class, @selector(viewDidAppear:));
IMP implementationReplacementIMP = replacementImplementationGenerator(viewdidAppear);
替換為視圖出現(xiàn)時調(diào)用的方法,如果在掉用deallocating后,viewdidAppear被調(diào)用則能夠證明上面所指之處就是我們觸發(fā)dealloc的方法。
替換前的打?。?/p>
我來了
準(zhǔn)備走了
SecondController 銷毀
已經(jīng)走了
替換后的打印:
我來了
準(zhǔn)備走了
我來了
通過以上測試能夠確定dealloc就是在代碼塊中調(diào)用的。注意在修改源碼后要clean一下工程,否則緩存會影響執(zhí)行結(jié)果。
deallocated序列的創(chuàng)建
下面看看deallocated序列是如何產(chǎn)生,又是如何在dealloc調(diào)用完成之后執(zhí)行的。
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
}
}
- 關(guān)聯(lián)了創(chuàng)建的序列,保證當(dāng)前控制器內(nèi)的序列對象只有一個
DeallocObservable代碼:
fileprivate final class DeallocObservable {
let _subject = ReplaySubject<Void>.create(bufferSize:1)
init() {
}
deinit {
self._subject.on(.next(()))
self._subject.on(.completed)
}
}
- 內(nèi)部也初始化了一個
ReplaySubject序列,用來發(fā)送消息 - 在對象銷毀時調(diào)用了
.next和.completed,這里不難理解,發(fā)送一條消息,再發(fā)送一條完成消息終止序列,因此在外部創(chuàng)建序列不需要添加垃圾袋
總結(jié)
- 在
RxSwift中提供了兩個關(guān)于dealloc(deinit)的序列,觀察dealloc的調(diào)用,其中deallocating內(nèi)部替換了原生的dealloc方法從而達(dá)到監(jiān)聽dealloc的調(diào)用 - 這里并不是交換方法,而是在
replacementImplementationGenerator代碼塊中先保留了dealloc的函數(shù)地址,再通過imp_implementationWithBlock設(shè)置dealloc的IMP,指向了replacementImplementationGenerator代碼塊 - 調(diào)用
dealloc方法就會調(diào)用了代碼塊,在代碼塊內(nèi)部通過body函數(shù)調(diào)用了deallocating方法,之后執(zhí)行代碼塊中保留的原dealloc函數(shù)