RxSwift-中介者模式&deallocating
我们先由timer的创建使用,引出中介者模式,进而扩展介绍Rxswift的中介者模式使用。
首先,我们来看timer的几种创建方式如下:
1.常用的timer创建,加入到runloop中
let timer = Timer.init(timeInterval: 1, target: self, selector: #selector(timerFire), userInfo: nibName, repeats: true)
RunLoop.current.add(timer, forMode: .common)
2.CADisplayLink
let displayLink = CADisplayLink(target: self, selector: #selector(timerFire))
displayLink.preferredFramesPerSecond = 1
displayLink.add(to: RunLoop.current, forMode: .default)
3.GCDTimer
gcdTimer = DispatchSource.makeTimerSource()
gcdTimer?.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.seconds(1))
gcdTimer?.setEventHandler(handler: {
print("hello GCD")
})
gcdTimer?.resume()
4.RxSwift的timer
timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
_ = observable?.subscribe(onNext:{(number) in
}).disposed(by: disposeBag)
注意: 1,2的创建方式需要加入到runloop中,runloopMode指定为default时,UI操作会影响计时准确度,runloopMode指定为.common时,不受UI操作影响。
使用第一种方式创建timer时,如果需要self持有timer进行其他操作时,就会形成循环引用,因为在初始化时,将self作为target传值给timer,而self又持有timer。
下面我们将使用中介者模式来规避这个循环引用的问题
创建一个XQProxy类,如下
class XQProxy: NSObject {
weak var target : NSObjectProtocol?
var sel : Selector?
var xqTimer : Timer?
override init() {
super.init()
}
func xq_scheduleTimer(timeInterval interval:TimeInterval ,target innerTagert:Any ,selector aselector:Selector , userInfo :Any? , repeat yesOrNo:Bool) {
self.target = innerTagert as? NSObjectProtocol
self.sel = aselector
self.xqTimer = Timer.init(timeInterval: interval, target: self, selector: aselector, userInfo: userInfo, repeats: yesOrNo)
RunLoop.current.add(self.xqTimer!, forMode: .common)
guard self.target?.responds(to: self.sel)==true else {
return
}
let method = class_getInstanceMethod(self.classForCoder, #selector(xq_timerFire))
class_replaceMethod(self.classForCoder, self.sel!, method_getImplementation(method!), method_getTypeEncoding(method!))
}
@objc fileprivate func xq_timerFire () {
if self.target != nil {
self.target!.perform(self.sel)
}else{
self.xqTimer?.invalidate()
self.xqTimer = nil
}
}
}
外部调用代码如下,即可开启定时任务
xqProxy.xq_scheduleTimer(timeInterval: 1, target: self, selector: #selector(timerFunc), userInfo: nil, repeat: true)
在XQProxy中,打破了原有的self->timer->self的循环引用结构,新结构为self->XQProxy->timer->XQProxy--/weak/--self。XQProxy会在执行xq_timerFire时,检测是否还存在相应的target,如果不存在,就停止time,并且置为nil,打破time与proxy的互相持有关系。
总结: 引入XQProxy作为中介者,XQProxy使用weak持有target,不影响target的正常释放。XQProxy持有timer和调用者,就可以控制调用流程,这样就达到了中介者在中间调控的目的
RxSwift的deallocating分析
下面我们看下RxSwift的deallocating的调用流程
- 外部调用代码
_ = rx.deallocating
.subscribe(onNext:{() in
print("即将要释放")
})
- deallocating的实现如下,创建了一个proxy,并且调用proxy.messageSent返回一个序列,那么我们先分析self.registerMessageInterceptor(deallocSelector)执行了什么操作,
注意:定义deallocSelector的原因是在ARC下无法直接写#selector(dealloc),不允许重写dealloc,所以需要以string形式生成方法
private let deallocSelector = NSSelectorFromString("dealloc")
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)
}
}
}
- registerMessageInterceptor方法中,最重要的步骤是执行了RX_ensure_observing,其中隐藏了方法交换操作
fileprivate func registerMessageInterceptor<T: MessageInterceptorSubject>(_ selector: Selector) throws -> T {
var error: NSError?
let targetImplementation = RX_ensure_observing(self.base, selector, &error)
subject.targetImplementation = targetImplementation
return subject
}
- RX_ensure_observing的前面都是为了调用加锁,保证执行的安全性,这里之所以使用oc代码是因为要使用到runtime,直接查看[self ensurePrepared:target forObserving:selector error:error]的实现
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;
}
- ensurePrepared方法的核心实现如下,调用[self swizzleDeallocating:deallocSwizzingTarget error:error]
-(IMP __nullable)ensurePrepared:(id __nonnull)target forObserving:(SEL __nonnull)selector error:(NSErrorParam)error {
if (selector == deallocSelector) {
if (![self swizzleDeallocating:deallocSwizzingTarget error:error]) {
return nil;
}
}
}
- 点击进入swizzleDeallocating会看到一个宏定义如下,此处使用宏定义的目的是为了在编译时刻就生成对应的方法,提高运行效率
SWIZZLE_INFRASTRUCTURE_METHOD(
void,
swizzleDeallocating,
,
deallocSelector,
DEALLOCATING_BODY
)
- 下面宏定义将method_name转化为-(BOOL)method_name:(Class __nonnull)class parameters,
#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__)
- SWIZZLE_METHOD的定义如下,为了更方便查看,去除了定义中的\。其实质是生成两个函数newImplementationGenerator与replacementImplementationGenerator,并且调用[self ensureSwizzledSelector:selector ofClass:class newImplementationGenerator:newImplementationGenerator replacementImplementationGenerator:replacementImplementationGenerator error:error];
#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];
}
- 在新生成的方法中调用了body(VA_ARGS),这个参数为DEALLOCATING_BODY宏定义,即调用[observer deallocating];
#define DEALLOCATING_BODY(...) \
id<RXDeallocatingObserver> observer = objc_getAssociatedObject(self, rxSelector); \
if (observer != nil && observer.targetImplementation == thisIMP) { \
[observer deallocating]; \
}
- ensureSwizzledSelector方法中,class_addMethod(class, selector, newImplementation, encoding)是为类的添加新的方法实现,其中,class为当前类,selector为deallocSelector,newImplementation是定义的新的实现,由第9步知道新的实现先调用了[observer deallocating]
-(BOOL)ensureSwizzledSelector:(SEL __nonnull)selector
ofClass:(Class __nonnull)class
newImplementationGenerator:(IMP(^)(void))newImplementationGenerator
replacementImplementationGenerator:(IMP (^)(IMP originalImplementation))replacementImplementationGenerator
error:(NSErrorParam)error {
IMP newImplementation = newImplementationGenerator();
if (class_addMethod(class, selector, newImplementation, encoding)) {
// new method added, job done
[self registerInterceptedSelector:selector implementation:newImplementation forClass:class];
return YES;
}
return YES;
}
- 第2步中,proxy对象遵循的协议DeallocatingProxy定义deallocating调用self.messageSent.on(.next(())),messageSent即为ReplaySubject对象,根据RxSwift核心流程,调用on(.next)等同于调用外部订阅的onNext方法
fileprivate final class DeallocatingProxy : MessageInterceptorSubject, RXDeallocatingObserver {
typealias Element = ()
let messageSent = ReplaySubject<()>.create(bufferSize: 1)
@objc func deallocating() {
self.messageSent.on(.next(()))
}
}
总结,RxSwift使用DeallocatingProxy类,创建出ReplaySubject,交换更新原有类的dealloc方法,在调用时先触发外部订阅的ReplaySubject.onNext,然后再释放对象,达到了由DeallocatingProxy控制释放流程通知外部的机制。