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的调用流程

  1. 外部调用代码
_ = rx.deallocating
    .subscribe(onNext:{() in
        print("即将要释放")
    })
  1. 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)
            }
        }
    }
  1. 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
    }
  1. 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;
}
  1. 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;
        }
    }
}
  1. 点击进入swizzleDeallocating会看到一个宏定义如下,此处使用宏定义的目的是为了在编译时刻就生成对应的方法,提高运行效率
SWIZZLE_INFRASTRUCTURE_METHOD(
    void,
    swizzleDeallocating,
    ,
    deallocSelector,
    DEALLOCATING_BODY
)
  1. 下面宏定义将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__)   
  1. 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]; 
 }                                                                      
  1. 在新生成的方法中调用了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];                                                      \
    }
  1. 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;
}
  1. 第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控制释放流程通知外部的机制。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354