KVO底层探索请参考文章 KVO底层探索。
RxSwift对KVO的调用主要有两种方式:
-
rx.observe()
:
1、对KVO的封装相对简单,高效。
2、路径不能包含weak
属性,否则有可能会造成崩溃
-
rx.observeWeakly()
:
1、对KVO的封装相对复杂一些,处理了对象的弱引用防止释放
2、可以用在weak
属性上
3、使用rx.observe()
的地方都可以使用rx.observeWeakly()
下面贴出使用代码 代码-001 :
//代码-001
self.person.rx.observeWeakly(String.self, "name")
.subscribe(onNext: { (value) in
print("KVO:\(String(describing: value))")
})
.disposed(by: disposeBag)
一、.observeWeakly()
首先点击进入.observeWeakly()
方法,来到Reactive
结构体的扩展方法里面 代码-002 :
//代码-002
public func observeWeakly<Element>(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial]) -> Observable<Element?> {
return observeWeaklyKeyPathFor(self.base, keyPath: keyPath, options: options)
.map { n in
return n as? Element
}
}
-
self.base
就是我们的person
对象。 -
keyPath
就是name
-
options
的值是[.new, .initial]
点击observeWeaklyKeyPathFor (self.base, keyPath: keyPath, options: options)
方法 代码-003 :
//代码-003
private func observeWeaklyKeyPathFor(_ target: NSObject, keyPath: String, options: KeyValueObservingOptions) -> Observable<AnyObject?> {
let components = keyPath.components(separatedBy: ".").filter { $0 != "self" }
let observable = observeWeaklyKeyPathFor(target, keyPathSections: components, options: options)
.finishWithNilWhenDealloc(target)
if !options.isDisjoint(with: .initial) {
return observable
}
else {
return observable
.skip(1)
}
}
- 1、将
keyPath
解析放到components
数组里 - 2、
bserveWeaklyKeyPathFor(target, keyPathSections: components, options: options) .finishWithNilWhenDealloc(target)
创建observable
序列 - 3、
options.isDisjoint(with: .initial)
判断是否存在.initial
值,存在返回false
下面我们来重点看一下第二部如何创建observable
序列,点击进入 代码-004 :
//代码-004
private func observeWeaklyKeyPathFor(
_ target: NSObject,
keyPathSections: [String],
options: KeyValueObservingOptions
) -> Observable<AnyObject?> {
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>
// KVO recursion for value changes
return propertyObservable
.flatMapLatest { (nextTarget: AnyObject?) -> Observable<AnyObject?> in
if nextTarget == nil {
return Observable.just(nil)
}
let nextObject = nextTarget! as? NSObject
let strongTarget: AnyObject? = weakTarget
if nextObject == nil {
return Observable.error(RxCocoaError.invalidObjectOnKeyPath(object: nextTarget!, sourceObject: strongTarget ?? NSNull(), propertyName: propertyName))
}
// if target is alive, then send change
// if it's deallocated, don't send anything
if strongTarget == nil {
return Observable.empty()
}
let nextElementsObservable = keyPathSections.count == 1
? Observable.just(nextTarget)
: observeWeaklyKeyPathFor(nextObject!, keyPathSections: remainingPaths, options: options)
if isWeak {
return nextElementsObservable
.finishWithNilWhenDealloc(nextObject!)
}
else {
return nextElementsObservable
}
}
}
- 1、首先弱引用
weakTarget
- 2、
let property = class_getProperty(object_getClass(target), propertyName)
获取target
中对应keyPath
的属性 - 3、
let propertyAttributes = property_getAttributes(property!)
获取属性的Attributes
- 4、
let isWeak = isWeakProperty(propertyAttributes.map(String.init) ?? "")
判断是否是弱引用 - 5、创建
KVOObservable
类型的序列propertyObservable
- 6、返回
propertyObservable
序列
我们看下创建KVOObservable
类型的序列propertyObservable
代码 代码-005:
//代码-005
fileprivate final class KVOObservable<Element>
: ObservableType
, KVOObservableProtocol {
typealias Element = Element?
unowned var target: AnyObject
var strongTarget: AnyObject?
var keyPath: String
var options: KeyValueObservingOptions
var retainTarget: Bool
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
}
}
func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element? {
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)
}
}
- 1、
target
的持有使用unowned
修饰,弱引用,一旦被释放不会被置为nil
,再调用就会崩溃,相比weak
来说性能更高。 - 2、该类内实现了
subscribe
方法,序列的常规操作。
看一下 代码-003 中的.finishWithNilWhenDealloc(target)
方法实现,点击进入 代码-006:
//代码-006
fileprivate extension ObservableType where Element == AnyObject? {
func finishWithNilWhenDealloc(_ target: NSObject)
-> Observable<AnyObject?> {
let deallocating = target.rx.deallocating
return deallocating
.map { _ in
return Observable.just(nil)
}
.startWith(self.asObservable())
.switchLatest()
}
}
我们先按字面意思理解一下,销毁时将target
设置为nil
。
二、.subscribe({ })
对RxSwift有了一定的了解,这个地方很明显,我们直接查看 代码-005 内的方法实现:
- 1、 创建
KVOObserver
类型的监听者observer
- 2、返回
Disposables
销毁者
继续查看KVOObserver
类内的代码 代码-007:
//代码-007
fileprivate final class KVOObserver
: _RXKVOObserver
, Disposable {
typealias Callback = (Any?) -> Void
var retainSelf: KVOObserver?
init(parent: KVOObservableProtocol, callback: @escaping Callback) {
super.init(target: parent.target, retainTarget: parent.retainTarget, keyPath: parent.keyPath, options: parent.options.nsOptions, callback: callback)
self.retainSelf = self
}
override func dispose() {
super.dispose()
self.retainSelf = nil
}
}
- 1、
KVOObserver
继承之_RXKVOObserver
- 2、遵守
Disposable
协议,目的是自我销毁
查看_RXKVOObserver
类内的代码 代码-008:
@implementation _RXKVOObserver
-(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;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
@synchronized(self) {
self.callback(change[NSKeyValueChangeNewKey]);
}
}
-(void)dispose {
[self.target removeObserver:self forKeyPath:self.keyPath context:nil];
self.target = nil;
self.retainedTarget = nil;
}
@end
发现是OC
代码,一共就实现了三个方法,这三个方法做了三件事:1、 添加观察 2、观察响应回调 3、移除观察。这就是KVO的三部曲呀,典型的中介者模式。
因为我们的options
包含了.initial
值,所以初始化的时候会执行一次self.callback(change[NSKeyValueChangeNewKey]);
方法。
这里的callBack
就是我们KVOObserver({ })
后面的闭包 代码-005 ,observer.on(.next(value as? Element))
执行,所以序列响应。
只要我们的序列销毁或者订阅关系的销毁的时候就会自动调用dispose
网友评论