RxSwift(9)—— KVO底层探索(下)

就问此时此刻还有谁?45度仰望天空,该死!我这无处安放的魅力!


RxSwift目录直通车--- 和谐学习,不急不躁!


上面一篇章我们对KVO底层有了一定了解!这一篇我们就开始分析RxSwiftKVO的封装,看完这一篇,你估计也会由衷的感慨:底层源码的思路是有相同的

RxSwift - KVO简介

RxSwiftKVO的调用主要有两种方式:

  • rx.observe:更加高效,因为它是一个 KVO 机制的简单封装。
  • rx.observeWeakly : 执行效率要低一些,因为它要处理对象的释放防止弱引用(对象的 dealloc 关系)。

应用场景:

  • 在可以使用 rx.observe 的地方都可以使用 rx.observeWeakly
  • 使用 rx.observe 时路径只能包括 strong 属性,否则就会有系统崩溃的风险。而 rx.observeWeakly 可以用在 weak 属性上。
self.person.rx.observeWeakly(String.self, "name")
            .subscribe(onNext: { (change) in
                print("observeWeakly订阅到了KVO:\(String(describing: change))")
            }).disposed(by: disposeBag)

使用起来非常简单,可读性高,免去传统KVO带来的恶心!下面也粘贴出代码对比一下

// 1: 添加观察
person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
// 2: 观察响应回调
override func observeValue(forKeyPath keyPath:, of object:, change: , context:){}
// 3: 移除观察
person.removeObserver(self, forKeyPath: "name")

RxSwift - KVO底层探索

首先分析在 RxSwift 的世界必然是由序列的,第一步分析序列的创建

中间的细节流程过滤,大家自己查看源码!我们直奔核心,马上要上课!哈哈哈~~~~~~

let observable = observeWeaklyKeyPathFor(target, keyPathSections: components, options: options)
    .finishWithNilWhenDealloc(target)
  • observeWeaklyKeyPathFor内部创建序列
  • finishWithNilWhenDealloc 针对提前释放的变量的容错,如果对象释放,也就没有观察的必要,写的很不错,毕竟能够观察weak修饰对象
weak var weakTarget: AnyObject? = target

let propertyName = keyPathSections[0]
let remainingPaths = Array(keyPathSections[1..<keyPathSections.count])

let property = class_getProperty(object_getClass(target), propertyName)
if property == nil {
    return Observable.error(RxCocoaError.invalidPropertyName(object: target, propertyName: propertyName))
}
let propertyAttributes = property_getAttributes(property!)

// should dealloc hook be in place if week property, or just create strong reference because it doesn't matter
let isWeak = isWeakProperty(propertyAttributes.map(String.init) ?? "")

let propertyObservable = KVOObservable(object: target, keyPath: propertyName, options: options.union(.initial), retainTarget: false) as KVOObservable<AnyObject>
  • 这段代码主要针对观察的keyPath进行处理分析
  • KVOObservable 就是我们的KVO观察的序列,这个对象继承ObservableType,让其具备序列的特性。实现KVOObservableProtocol协议,拓展几个协议属性,常规面向协议编程的思路
init(object: AnyObject, keyPath: String, options: KeyValueObservingOptions, retainTarget: Bool) {
    self.target = object
    self.keyPath = keyPath
    self.options = options
    self.retainTarget = retainTarget
    if retainTarget {
        self.strongTarget = object
    }
}
  • 这段代码,想必大家看完上一篇文章之后非常容易理解,就是我们KVO信息保存者
  • 初始化完毕就是外界flatMapLatest的封装容错

序列已经创建完毕,下面开始分析订阅,响应发送

常规订阅,提供给内部:AnonymousObserver,这里不讲了,前面的流程非常简单---直接分析重点

func subscribe(_ observer: Observer) -> Disposable {
    let observer = KVOObserver(parent: self) { value in
        if value as? NSNull != nil {
            observer.on(.next(nil))
            return
        }
        observer.on(.next(value as? Element))
    }

    return Disposables.create(with: observer.dispose)
}
  • 核心重点类KVOObserver
  • 保存了一个闭包,我们先看里面,毕竟这里还不能执行
  • KVOObserver这段继承关系是非常重要的
  • 继承_RXKVOObserver,我们查看内部,我的天啊!竟然是一个OC类
  • 实现Disposable协议,具备销毁的能力,用来干嘛,等会讲解
-(instancetype)initWithTarget:(id)target
                 retainTarget:(BOOL)retainTarget
                      keyPath:(NSString*)keyPath
                      options:(NSKeyValueObservingOptions)options
                     callback:(void (^)(id))callback {
    self = [super init];
    if (!self) return nil;
    
    self.target = target;
    if (retainTarget) {
        self.retainedTarget = target;
    }
    self.keyPath = keyPath;
    self.callback = callback;
    
    // 核心骚操作
    [self.target addObserver:self forKeyPath:self.keyPath options:options context:nil];
    
    return self;
}
  • 这里面针对外界的一些KVO信息处理保存,callback回调的保存,函数式思想
  • [self.target addObserver:self forKeyPath:self.keyPath options:options context:nil];这段代码是比较骚,核心逻辑:观察者移交,我们不在是VC观察,使我们内部类!
  • 观察的keypath有了变化,必然胡响应下面的方法
-(void)observeValueForKeyPath: ofObject: change: context: {
    @synchronized(self) {
        self.callback(change[NSKeyValueChangeNewKey]);
    }
}
  • 重点就是之前保存的回到函数调用self.callback(change[NSKeyValueChangeNewKey]),那么我们的流程就会流到之前的对象初始化时候的闭包
let observer = KVOObserver(parent: self) { value in
    if value as? NSNull != nil {
        observer.on(.next(nil))
        return
    }
    observer.on(.next(value as? Element))
}
  • 我们观察者直接进行了发送响应,这里的value值就是KVO回调的change,完美!序列订阅得以响应

下面还缺一个点:关于KVORxSwift的世界里面是不需要移除观察的,下面开始解析

override func dispose() {
    super.dispose()
    self.retainSelf = nil
}
  • 这个dispose的流程是非常简单了,前面我们也有相关分析
  • 核心就是调用了 super.dispose()
-(void)dispose {
    [self.target removeObserver:self forKeyPath:self.keyPath context:nil];
    self.target = nil;
    self.retainedTarget = nil;
}
  • 只要我们的序列销毁或者订阅关系的销毁的时候就会自动调用dispose
  • 完美看到观察的移除!

总结:**纵观RxSwift的KVO流程也就是中间者模式! 移交观察者,到达响应效果。

就问此时此刻还有谁?45度仰望天空,该死!我这无处安放的魅力!

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