美文网首页
RxSwift-KVO

RxSwift-KVO

作者: Code_人生 | 来源:发表于2019-08-06 14:43 被阅读0次

一、Swift-KVO

  • 步骤一、添加观察
    func swiftKVO() {
        person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
    }
  • 步骤二、观察回调
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        print(change!)
    }
  • 步骤三、移除观察
    deinit {
        person.removeObserver(self, forKeyPath: "name")
    }
  • 注意:监听的属性前 必须 加上 @objc dynamic
import UIKit

class Person: NSObject {
    @objc dynamic var name: String = "dyz"
}

二、RxSwift-KVO

  • 2.1、observe
        self.person.rx.observe(String.self, "name").subscribe(onNext: { (value) in
            print(value!)
        }).disposed(by: disposeBag)
  • 2.2、observeWeakly
        self.person.rx.observeWeakly(String.self, "name").subscribe(onNext: { (value) in
            print(value!)
        })
        .disposed(by: disposeBag)

分析 observeWeakly

#if !DISABLE_SWIZZLING && !os(Linux)
// KVO
extension Reactive where Base: NSObject {
    //省略...
    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
            }
    }
}
#endif
  • target:被观察者Person
    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)
        }
    }
  • 点击finishWithNilWhenDealloc
    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()
        }
    }
  • 点击observeWeaklyKeyPathFor来到下面代码
  • 点击KVOObservable
    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
                }
        }
    }
  • KVOObservable 实现了KVOObservableProtocol协议,拥有了target、keyPath、retainTarget、options等属性
  • 外界调用subscribe(onNext:...),RxSwift 核心逻辑,必然会来到下面的subscribe方法
  • subscribe中初始化KVOObserver观察者和保存闭包(闭包的回调是在KVO收到响应的时候)
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)
    }

}

fileprivate extension KeyValueObservingOptions {
    var nsOptions: NSKeyValueObservingOptions {
        var result: UInt = 0
        if self.contains(.new) {
            result |= NSKeyValueObservingOptions.new.rawValue
        }
        if self.contains(.initial) {
            result |= NSKeyValueObservingOptions.initial.rawValue
        }
        
        return NSKeyValueObservingOptions(rawValue: result)
    }
}
private protocol KVOObservableProtocol {
    var target: AnyObject { get }
    var keyPath: String { get }
    var retainTarget: Bool { get }
    var options: KeyValueObservingOptions { get }
}
  • 点击KVOObserver,来到KVOObserver类,它继承_RXKVOObserver类
  • 调用父类_RXKVOObserver的初始化方法,把值和闭包都传给父类
  • dispose方法,会调用父类_RXKVOObserver的dispose方法,从而移除观察
fileprivate final class KVOObserver
    : _RXKVOObserver
    , Disposable {
    typealias Callback = (Any?) -> Void

    var retainSelf: KVOObserver?

    init(parent: KVOObservableProtocol, callback: @escaping Callback) {
        #if TRACE_RESOURCES
            _ = Resources.incrementTotal()
        #endif

        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
    }

    deinit {
        #if TRACE_RESOURCES
            _ = Resources.decrementTotal()
        #endif
    }
}
  • _RXKVOObserver类是一个OC类
@interface _RXKVOObserver : NSObject

-(instancetype)initWithTarget:(id)target
                 retainTarget:(BOOL)retainTarget
                      keyPath:(NSString*)keyPath
                      options:(NSKeyValueObservingOptions)options
                     callback:(void (^)(id))callback;

-(void)dispose;

@end
  • 添加观察:观察者移交[self.target addObserver:self forKeyPath:self.keyPath options:options context:nil];
  • 观察回调:-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {},执行保存的闭包
  • 移除观察:[self.target removeObserver:self forKeyPath:self.keyPath context:nil];
@interface _RXKVOObserver ()

@property (nonatomic, unsafe_unretained) id            target;
@property (nonatomic, strong           ) id            retainedTarget;
@property (nonatomic, copy             ) NSString     *keyPath;
@property (nonatomic, copy             ) void (^callback)(id);

@end

@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

重点:RX-SwiftKVO其实是封装的是OC的KVO,Swift的封装的也是OC的KVO

相关文章

  • RxSwift-KVO

    一、Swift-KVO 步骤一、添加观察 步骤二、观察回调 步骤三、移除观察 注意:监听的属性前 必须 加上 @o...

  • RxSwift-KVO\内存管理

    函数响应编程&RxSwift核心逻辑 上函数响应编程&RxSwift核心逻辑 下待续...正在努力编写RxSwif...

  • RxSwift-KVO底层探索(上)

    KVO在我们实际开发之中运用非常之多,很多开发者都知道原理!但是这些原理是如何来的,一般都是浅尝辄止。这个篇章我会...

  • RxSwift-KVO底层探索(下)

    上面一篇章我们对KVO底层有了一定了解!这一篇我们就开始分析RxSwift对KVO的封装,看完这一篇,你估计也会由...

  • 详解:RxSwift-KVO底层探索(上)

    收录:原文地址 KVO在我们实际开发之中运用非常之多,很多开发者都知道原理!但是这些原理是如何来的,一般都是浅尝辄...

  • 详解:RxSwift-KVO底层探索(下)

    收录:原文地址 上一个篇章我们对KVO底层有了一定了解!这一篇我们就开始分析RxSwift对KVO的封装,看完这一...

网友评论

      本文标题:RxSwift-KVO

      本文链接:https://www.haomeiwen.com/subject/aioidctx.html