簡述
本文是對上一篇文章的補充,加上對KVO實現的簡要分析 和 自己的一些思考,可能會有不夠準確的地方還望多多指正,共同學習~~
框架結構
在上篇文章中是從具體實現的角度分析Observable的使用實現,接下來從一個高一點的角度來分析RXSwift.
效果
無論是Observable,還是KVO,通知,按鈕點選….,在本質上我認為可將這一切看為這樣一個過程~
以通知為例子, 在程式中某個地方發出了事件(例如鍵盤彈出),這就是事件源. 然後這個事件傳遞(系統發出鍵盤彈出的通知). 最後程式的某處響應了這個事件(比如我們監聽到這個通知,然後將控制元件上移). 我認為RXSwift就是為了讓大家更方便的實現這樣的過程~
而RXSwift的結構就大概是這樣的
結構簡圖
1,事件源
例如create
函式~
2,響應
為了簡潔,我並沒有加入資源釋放那部分, 具體的可以參照上篇進行對比的來看, 中介在事件源中接受事件,在響應中輸出事件.
接下啦我再以KVO的實現細節 再來展示一下這個結構~~
KVO實現細節
下面是一段日常使用RXSwift的程式碼~
class Test: NSObject {
@objc var name:String!
}
class ViewController: UIViewController {
@objc var obj:Test!
var disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
obj = Test()
obj.rx.observe(String.self, #keyPath(Test.name)).subscribe { (eve) in
print(eve)
}.disposed(by: self.disposeBag)
}
}
複製程式碼
事件源
rx
首先第一步是呼叫物件的rx屬性, rx來源於 對NSObject的擴充套件協議~
import class Foundation.NSObject
extension NSObject: ReactiveCompatible { }
複製程式碼
協議實現
public protocol ReactiveCompatible {
associatedtype CompatibleType
// 類屬性 和 物件屬性~
static var rx: Reactive<CompatibleType>.Type { get set }
var rx: Reactive<CompatibleType> { get set }
}
extension ReactiveCompatible {
public static var rx: Reactive<Self>.Type {
get {
return Reactive<Self>.self
}
set {
}
}
public var rx: Reactive<Self> {
get {
return Reactive(self)
}
set {
}
}
}
複製程式碼
這裡其實是為了將當前物件封裝為一個**Reactive**結構體
public struct Reactive<Base> {
public let base: Base
public init(_ base: Base) {
self.base = base
}
}
複製程式碼
observe
一般我們呼叫的是這個擴充套件方法
extension Reactive where Base: NSObject {
public func observe<E>(_ type: E.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial], retainSelf: Bool = true) -> Observable<E?> {
return KVOObservable(object: base, keyPath: keyPath, options: options, retainTarget: retainSelf).asObservable()
}
}
複製程式碼
observe
方法首先建立一個KVOObservable
物件~
//YSD--產生KVO的可觀察者
fileprivate final class KVOObservable<Element>: ObservableType
, KVOObservableProtocol {
typealias E = 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<O : ObserverType>(_ observer: O) -> Disposable where O.E == 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 protocol KVOObservableProtocol {
var target: AnyObject { get }
var keyPath: String { get }
var retainTarget: Bool { get }
var options: KeyValueObservingOptions { get }
}
複製程式碼
KVOObservable
物件遵守ObservableType
協議,所以可以呼叫asObsevable()
方法, KVOObservableProtocol
協議限定它持有這些KVO 必須的屬性~,因為在本質上,還是呼叫OC的KVO實現~
動態語言還是爽呀~
訂閱方法
訂閱時也就是上面過載的方法~ 首先還是建立一個觀察者,來持有 事件響應的閉包~
let observer = KVOObserver(parent: self) { (value) in
if value as? NSNull != nil {
observer.on(.next(nil))
return
}
observer.on(.next(value as? Element))
}
複製程式碼
然而事件是從哪裡發出的呢~
fileprivate final class KVOObserver
: _RXKVOObserver
, Disposable {
typealias Callback = (Any?) -> Void
var retainSelf: KVOObserver? = nil
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
}
//只用呼叫dispose後才會回收,所以大家注意 不要因為偷懶不好好使用disposeBag(๑•ᴗ•๑)
override func dispose() {
super.dispose()
self.retainSelf = nil
}
deinit {
#if TRACE_RESOURCES
_ = Resources.decrementTotal()
#endif
}
}
複製程式碼
相對應它的父類就是OC實現的~
@interface _RXKVOObserver ()
//和weak差不多, 但是weak釋放了為變為nil 它不會 會因為野指標的使用而崩潰
@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;
}
//常規的操作,將監聽到的新值作為block引數返回
-(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;
}
複製程式碼
將KVO的newValue作為引數傳入 callBack閉包中~
所以RXSwift對於 KVO的實現就比較簡單了,觀察者既是事件源 也是 中介 總的來說是將 普通的KVO寫法 進行封裝,納入自己的體系之下
反思~
學而不思則罔,思而不學則殆. 所以看完大神的原始碼一定要反思~ 不然除了框架用的更熟練 跟沒看一樣~
面向協議
我認為RXSwift就是一個我們學習面向協議程式設計(Protocol-oriented programming)的好例子~, 通過協議 我們可以很好的解決繼承帶來的種種弊端~
1, 可以實現在OC和Swift不允許的 多繼承~
2, 避免基類受制於子類, 實現依賴倒轉, 讓子類受制於協議~
在RXSwift中有一些重要的協議ObservableType,ObserverType,Disposable
ObservableType
public protocol ObservableType : ObservableConvertibleType {
func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E
}
複製程式碼
ObserverType
public protocol ObserverType {
associatedtype E
func on(_ event: Event<E>)
}
extension ObserverType {
public func onNext(_ element: E) {
on(.next(element))
}
public func onCompleted() {
on(.completed)
}
public func onError(_ error: Swift.Error) {
on(.error(error))
}
}
複製程式碼
Disposable
public protocol Disposable {
public func dispose()
}
複製程式碼
通過這些協議 將各個角色類的行為加以限制, 而各個協議之間是互相對接的,這樣即使各個類不相同,只要大家遵守相同的協議,就可以在這個體系下暢通無阻~ . 打個簡單的例子就是 彈幕功能
從伺服器發來的需要展示的訊息是各種各樣的(普通彈幕,禮物彈幕,貴族彈幕,管理資訊彈幕~~~~),當其實在顯示的時候,只需要顯示文字和圖片而已~ 這樣讓 訊息都遵守可以獲取文字圖片的協議,這樣不管發過來什麼訊息 都可以正常顯示~
當然使用繼承也可以實現,但是若我們要加新的訊息型別(禮物火箭),這時繼承要改基類,亂改基類很有可能會影響到其他子類導致Bug,而協議只需要擴充套件,或者限定型別的擴充套件~
當然這不是說讓大家不要用繼承,在RXSwift中也是有繼承的~
final class AnonymousObserver<ElementType> : ObserverBase<ElementType> {......}
複製程式碼
所以我個人是這樣認為的,POP這是綱領,不是方案~ 使用的時候要靈活,小範圍,少變動的用繼承,大範圍,多變化的用協議
單一責任原則
在搭架子的時候就將 角色責任 區分好~ 就像最上面的圖示一樣,,避免類兼數值. 這樣無論是對Bug的定位,還是對專案架構的貫徹 都是有好處的~(๑•ᴗ•๑)
最少知道原則
也就是耦合度的問題,我覺得大家都知道寫程式碼要 高內聚,低耦合. 但是怎麼做呢~ 在RXSwift中是這樣做的,也就是POP 各個角色間通過協議溝通(我到目前為止展示出來的方法,基本上都是協議上的方法),而類通過遵守協議對協議內容進行實現. 這樣 耦合的就只有協議,而類只專注對協議的實現
結尾
暫時就反思了這麼多~然後有內容再補充吧,下篇就寫 flatMap的分析~ 如果老大的需求沒下來 應該很快吧~. 文中要有不準確的地方,請多多指正呀(๑•ᴗ•๑)
才發現掘金可以加封面大圖~~~藍瘦~