Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RxSwiftのDisposable達をご紹介

 RxSwiftのDisposable達をご紹介

CA.swift #8 にて発表した資料です
https://cyberagent.connpass.com/event/120746/

5405b3d871f38164ef419e95e9467197?s=128

Hideki Matsuoka

March 06, 2019
Tweet

Transcript

  1. RxSwiftの Disposable達をご紹介 株式会社マッチングエージェント 松岡 秀樹 Twitter: matsuokah_

  2. 松岡 秀樹 (Hideki Matsuoka) • 携わったプロダクト アメーバピグ(バックエンド) PiggPARTY(iOS/Android) famchatty(Android) OPENREC.tv(iOS)

    Tapple(iOS) • ⼀⾔ 最近、⾃宅にエルゴヒューマン買った。いい椅⼦はいい ぞ! ◀ イマココ
  3. 直近の登壇: iOSDC https://speakerdeck.com/matsuokah/live-streaming-with-screen-recording

  4. アジェンダ • Disposableとは • 基本的なDisposableの種類と使い所 • 応⽤的なDisposableの種類と使い所 • おまけ(時間が余ったら)

  5. 質問です

  6. Q . RxSwiftを触ったことがない⼈

  7. Observable.create { _ in … } Q . Observable.createしたことある⼈

  8. Observable.create { _ in return Disposables.create() } Q . Disposables.create()したことあるひと

  9. Observable.create { _ in return Disposables.create { } } Q

    . Disposables.create { }したことあるひと
  10. let disposable = Disposables.create() let disposable = Disposables.create { }

    Q . 違いがわかる⼈
  11. let disposable = Disposables.create() let serial = SerialDisposable() serial.disposable =

    disposable let single = SingleAssignmentDisposable() single.setDisposable(disposable) let refCount = RefCountDisposable() ref1 = refCount.retain() let scheduled = ScheduledDisposable(MainScheduler.instance, disposable: disposable) Q . Disposables.create以外のDisposableを使ったことがある⼈
  12. Disposables.create( Disposables.create { print(1) }, Disposables.create { print(2) }, Disposables.create

    { print(3) }, Disposables.create { print(4) }, Disposables.create { print(5) } ).dispose() Q . 実⾏順序がわかる⼈
  13. Disposablesとは

  14. Disposableとは • RxSwiftにおいてストリームのライフサイクルを 管理するためのもの • Disposableチェーンによって上流のdisposeを ⾏う • Disposable内にdispose時の処理を定義するこ とで、キャンセル処理を⾏うことができる

    ※イメージ
  15. 基本的なDisposable

  16. 基本的なDisposable • AnonymousDisposable • NopDisposable • BinaryDisposable • CompositeDisposable

  17. AnonymousDisposable • disposeされた時の処理を定義できるDisposable • Disposables.create { }の実体 extension Reactive where

    Base: SessionManager { func request<R: RxAlamofireRequest>(_ createRequest: @escaping (SessionManager) throws -> R) -> Observable<R> { return Observable.create { observer -> Disposable in let request: R do { request = try createRequest(self.base) ... return Disposables.create { request.cancel() } } catch { … } } } } https://github.com/RxSwiftCommunity/RxAlamofire/blob/master/Sources/RxAlamofire.swift#L -L リクエストを発⾏したあと、完了するまでに disposeされた時、APIリクエストをキャンセルする
  18. NopDisposable • disposeされても何もしないDisposable • Disposables.create()の実体 • シングルトン extension Reactive where

    Base: SessionManager { func request<R: RxAlamofireRequest>(_ createRequest: @escaping (SessionManager) throws -> R) -> Observable<R> { return Observable.create { observer -> Disposable in let request: R do { // ԿΒ͔ͷॲཧ } catch let error { observer.on(.error(error)) return Disposables.create() } } } } https://github.com/RxSwiftCommunity/RxAlamofire/blob/master/Sources/RxAlamofire.swift#L -L エラーを流して、あとは何もしない
  19. let disposable = Disposables.create() let disposable = Disposables.create { }

    Q . 違いがわかる⼈ 実⾏結果は変わらないが、Nopを使った⽅がパフォーマンスはいい
  20. BinaryDisposable • Disposableを2つ保持しておき、disposeされた時に保持してい るDisposableを両⽅disposeする • Disposables.create(disposable , disposable )の実体 Binary

    StreamA Subscription StreamB Subscription dispose() dispose() dispose()
  21. extension ObservableType { public func subscribe(onNext: ((E) -> Void)? =

    nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil) -> Disposable { let disposable: Disposable if let disposed = onDisposed { disposable = Disposables.create(with: disposed) } else { disposable = Disposables.create() } let observer = AnonymousObserver<E> { event in switch event { case .next(let value): onNext?(value) case .error(let error): if let onError = onError { onError(error) } disposable.dispose() case .completed: onCompleted?() disposable.dispose() } } return Disposables.create( self.asObservable().subscribe(observer), disposable ) } } disposablesと上流のsubscribeのラップ onDisposedのアクションをラップする AnonymousDisposable https://github.com/ReactiveX/RxSwift/blob/master/RxSwift/ObservableType% BExtensions.swift
  22. CompositeDisposable • 2つ以上のDisposableを保持しておき、disposeされた時に保持 している全てのDisposableをdisposeする Composition StreamA Subscription StreamB Subscription dispose()

    dispose() dispose() ‧‧‧
  23. final class CombineLatestSink3_<E1, E2, E3, O: ObserverType> : CombineLatestSink<O> {

    … func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() let observer1 = CombineLatestObserver(lock: self._lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) let observer2 = CombineLatestObserver(lock: self._lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) let observer3 = CombineLatestObserver(lock: self._lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) subscription1.setDisposable(self._parent._source1.subscribe(observer1)) subscription2.setDisposable(self._parent._source2.subscribe(observer2)) subscription3.setDisposable(self._parent._source3.subscribe(observer3)) return Disposables.create([ subscription1, subscription2, subscription3 ]) } … } CompositeDisposableでひとまとめにするので 下流では1つをdisposeすれば3つのDisposableに伝播する 上流のSubscriptionのラップ https://github.com/ReactiveX/RxSwift/blob/master/RxSwift/Observables/CombineLatest% Barity.swift#L -L
  24. extension Disposables { public static func create(_ disposable1: Disposable, _

    disposable2: Disposable, _ disposable3: Disposable) -> Cancelable { return CompositeDisposable(disposable1, disposable2, disposable3) } public static func create(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable, _ disposables: Disposable ...) -> Cancelable { var disposables = disposables disposables.append(disposable1) disposables.append(disposable2) disposables.append(disposable3) return CompositeDisposable(disposables: disposables) } /// Creates a disposable with the given disposables. public static func create(_ disposables: [Disposable]) -> Cancelable { switch disposables.count { case 2: return Disposables.create(disposables[0], disposables[1]) default: return CompositeDisposable(disposables: disposables) } } } CompositeDisposableのエイリアス https://github.com/ReactiveX/RxSwift/blob/master/RxSwift/Disposables/CompositeDisposable.swift#L -L ① ② 配列を使うと、BinaryへのエイリアスとDisposeの順番の担保ができる
  25. Disposables.create( Disposables.create { print(1) }, Disposables.create { print(2) }, Disposables.create

    { print(3) }, Disposables.create { print(4) }, Disposables.create { print(5) } ).dispose() Q . 実⾏順序がわかる⼈
  26. Disposables.create( Disposables.create { print(1) }, Disposables.create { print(2) }, Disposables.create

    { print(3) }, Disposables.create { print(4) }, Disposables.create { print(5) } ).dispose() Q . 実⾏順序がわかる⼈ 答えは という順番で表⽰される
  27. 応⽤的なDisposable

  28. 応⽤的なDisposable • SerialDisposable • SingleAssignmentDisposable • ScheduledDisposable

  29. SerialDisposable • Disposableを1つだけ保持するDisposableのラッパー • 新たにDisposableがセットされた時、古いDisposableを disposeする

  30. SingleAssignmentDisposable • 1度だけDisposableをセットできるDisposableのラッパー

  31. ScheduledDisposable • 指定されたスケジューラーでdisposeさせるためのDisposable

  32. final private class SubscribeOnSink<Ob: ObservableType, O: ObserverType>: Sink<O>, ObserverType where

    Ob.E == O.E { func run() -> Disposable { let disposeEverything = SerialDisposable() let cancelSchedule = SingleAssignmentDisposable() disposeEverything.disposable = cancelSchedule let disposeSchedule = self.parent.scheduler.schedule(()) { _ -> Disposable in let subscription = self.parent.source.subscribe(self) disposeEverything.disposable = ScheduledDisposable(scheduler: self.parent.scheduler, disposable: subscription) return Disposables.create() } cancelSchedule.setDisposable(disposeSchedule) return disposeEverything } } SubscribeOn.swiftを読む https://github.com/ReactiveX/RxSwift/blob/master/RxSwift/Observables/SubscribeOn.swift#L -L
  33. final private class SubscribeOnSink<Ob: ObservableType, O: ObserverType>: Sink<O>, ObserverType where

    Ob.E == O.E { func run() -> Disposable { let disposeEverything = SerialDisposable() let cancelSchedule = SingleAssignmentDisposable() disposeEverything.disposable = cancelSchedule let disposeSchedule = self.parent.scheduler.schedule(()) { _ -> Disposable in let subscription = self.parent.source.subscribe(self) disposeEverything.disposable = ScheduledDisposable(scheduler: self.parent.scheduler, disposable: subscription) return Disposables.create() } cancelSchedule.setDisposable(disposeSchedule) return disposeEverything } } SubscribeOnで使われているDisposable https://github.com/ReactiveX/RxSwift/blob/master/RxSwift/Observables/SubscribeOn.swift#L -L
  34. final private class SubscribeOnSink<Ob: ObservableType, O: ObserverType>: Sink<O>, ObserverType where

    Ob.E == O.E { func run() -> Disposable { let disposeEverything = SerialDisposable() let cancelSchedule = SingleAssignmentDisposable() disposeEverything.disposable = cancelSchedule let disposeSchedule = self.parent.scheduler.schedule(()) { _ -> Disposable in let subscription = self.parent.source.subscribe(self) disposeEverything.disposable = ScheduledDisposable(scheduler: self.parent.scheduler, disposable: subscription) return Disposables.create() } cancelSchedule.setDisposable(disposeSchedule) return disposeEverything } } SubscribeOnの処理の流れ ① scheduleされる前にdisposeされた場合 ③ scheduleをキャンセルさせるためのつなぎこみ ② 指定されたSchedulerで上流をSubscribeする https://github.com/ReactiveX/RxSwift/blob/master/RxSwift/Observables/SubscribeOn.swift#L -L
  35. disposeEverything cancelSchedule disposeSchedule SerialDisposable SingleAssignmentDisposable NopDisposable スケジュール前 スケジュール後

  36. disposeEverything cancelSchedule disposeSchedule SerialDisposable SingleAssignmentDisposable(disposed) NopDisposable disposeEverything scheduledDisposable parentDisposable SerialDisposable

    ScheduledDisposable AnyDisposable(上流のSubscription) scheduled スケジュール前 スケジュール後
  37. final private class SubscribeOnSink<Ob: ObservableType, O: ObserverType>: Sink<O>, ObserverType where

    Ob.E == O.E { func run() -> Disposable { let disposeEverything = SerialDisposable() let cancelSchedule = SingleAssignmentDisposable() disposeEverything.disposable = cancelSchedule let disposeSchedule = self.parent.scheduler.schedule(()) { _ -> Disposable in let subscription = self.parent.source.subscribe(self) disposeEverything.disposable = ScheduledDisposable(scheduler: self.parent.scheduler, disposable: subscription) return Disposables.create() } cancelSchedule.setDisposable(disposeSchedule) return disposeEverything } } SubscribeOn.swiftを読む https://github.com/ReactiveX/RxSwift/blob/master/RxSwift/Observables/SubscribeOn.swift#L -L ここで、cancelScheduleがdisposeされて ScheduledDisposableが管理対象に差し替えられる
  38. おまけ

  39. fileprivate final class AnonymousDisposable : DisposeBase, Cancelable { private var

    _isDisposed = AtomicInt(0) … fileprivate func dispose() { if fetchOr(&self._isDisposed, 1) == 0 { if let action = self._disposeAction { self._disposeAction = nil action() } } } } おまけ - RxAtomic
  40. おまけ - RxAtomic • stdatomic.hのラッパー • リード‧モディファイ‧ライトな操作にアトミック性を持たせ てくれる • 多重でdisposeのアクションが実⾏されることを防ぐ

  41. import RxAtomic typealias AtomicInt = RxAtomic.AtomicInt extension AtomicInt { public

    init(_ value: Int32) { self.init() AtomicInt_initialize(&self, value) } } @discardableResult @inline(__always) func fetchOr(_ this: UnsafeMutablePointer<AtomicInt>, _ mask: Int32) -> Int32 { return AtomicInt_fetchOr(this, mask) } おまけ - RxAtomic https://github.com/ReactiveX/RxSwift/blob/master/Platform/AtomicInt.swift#L -L
  42. #ifndef RxAtomic_h #define RxAtomic_h #include <stdatomic.h> #define SWIFT_NAME(_name) __attribute__((swift_name(#_name))) #define

    Atomic(swift_type, llvm_type) \ … static __inline__ __attribute__((__always_inline__)) \ llvm_type Atomic##swift_type##_fetchOr(Atomic##swift_type * _Nonnull self, llvm_type mask) { \ return atomic_fetch_or(&self->atom, mask);\ }\ … \ Atomic(Int, int) #undef SWIFT_NAME #endif /* RxAtomic_h */ おまけ - RxAtomic https://github.com/ReactiveX/RxSwift/blob/master/RxAtomic/include/RxAtomic.h#L -L
  43. まとめ • DisposableはRxのストリームのライフサイクル管理と後始末に 使われる • Disposableのはオペレータのライフサイクルや機能に応じて使 い分ける • RxAtomicではCのstdatomic.hをインライン展開している (今回発表にあたって読んでいて、初めて知った)

  44. ご静聴ありがとうございました