Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

松岡 秀樹 (Hideki Matsuoka) • 携わったプロダクト アメーバピグ(バックエンド) PiggPARTY(iOS/Android) famchatty(Android) OPENREC.tv(iOS) Tapple(iOS) • ⼀⾔ 最近、⾃宅にエルゴヒューマン買った。いい椅⼦はいい ぞ! ◀ イマココ

Slide 3

Slide 3 text

直近の登壇: iOSDC https://speakerdeck.com/matsuokah/live-streaming-with-screen-recording

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

質問です

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

let disposable = Disposables.create() let disposable = Disposables.create { } Q . 違いがわかる⼈

Slide 11

Slide 11 text

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を使ったことがある⼈

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Disposablesとは

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

基本的なDisposable

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

AnonymousDisposable • disposeされた時の処理を定義できるDisposable • Disposables.create { }の実体 extension Reactive where Base: SessionManager { func request(_ createRequest: @escaping (SessionManager) throws -> R) -> Observable { 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リクエストをキャンセルする

Slide 18

Slide 18 text

NopDisposable • disposeされても何もしないDisposable • Disposables.create()の実体 • シングルトン extension Reactive where Base: SessionManager { func request(_ createRequest: @escaping (SessionManager) throws -> R) -> Observable { 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 エラーを流して、あとは何もしない

Slide 19

Slide 19 text

let disposable = Disposables.create() let disposable = Disposables.create { } Q . 違いがわかる⼈ 実⾏結果は変わらないが、Nopを使った⽅がパフォーマンスはいい

Slide 20

Slide 20 text

BinaryDisposable • Disposableを2つ保持しておき、disposeされた時に保持してい るDisposableを両⽅disposeする • Disposables.create(disposable , disposable )の実体 Binary StreamA Subscription StreamB Subscription dispose() dispose() dispose()

Slide 21

Slide 21 text

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 { 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

Slide 22

Slide 22 text

CompositeDisposable • 2つ以上のDisposableを保持しておき、disposeされた時に保持 している全てのDisposableをdisposeする Composition StreamA Subscription StreamB Subscription dispose() dispose() dispose() ‧‧‧

Slide 23

Slide 23 text

final class CombineLatestSink3_ : CombineLatestSink { … 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

Slide 24

Slide 24 text

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の順番の担保ができる

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

応⽤的なDisposable

Slide 28

Slide 28 text

応⽤的なDisposable • SerialDisposable • SingleAssignmentDisposable • ScheduledDisposable

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

final private class SubscribeOnSink: Sink, 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

Slide 33

Slide 33 text

final private class SubscribeOnSink: Sink, 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

Slide 34

Slide 34 text

final private class SubscribeOnSink: Sink, 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

Slide 35

Slide 35 text

disposeEverything cancelSchedule disposeSchedule SerialDisposable SingleAssignmentDisposable NopDisposable スケジュール前 スケジュール後

Slide 36

Slide 36 text

disposeEverything cancelSchedule disposeSchedule SerialDisposable SingleAssignmentDisposable(disposed) NopDisposable disposeEverything scheduledDisposable parentDisposable SerialDisposable ScheduledDisposable AnyDisposable(上流のSubscription) scheduled スケジュール前 スケジュール後

Slide 37

Slide 37 text

final private class SubscribeOnSink: Sink, 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が管理対象に差し替えられる

Slide 38

Slide 38 text

おまけ

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

おまけ - RxAtomic • stdatomic.hのラッパー • リード‧モディファイ‧ライトな操作にアトミック性を持たせ てくれる • 多重でdisposeのアクションが実⾏されることを防ぐ

Slide 41

Slide 41 text

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, _ mask: Int32) -> Int32 { return AtomicInt_fetchOr(this, mask) } おまけ - RxAtomic https://github.com/ReactiveX/RxSwift/blob/master/Platform/AtomicInt.swift#L -L

Slide 42

Slide 42 text

#ifndef RxAtomic_h #define RxAtomic_h #include #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

Slide 43

Slide 43 text

まとめ • DisposableはRxのストリームのライフサイクル管理と後始末に 使われる • Disposableのはオペレータのライフサイクルや機能に応じて使 い分ける • RxAtomicではCのstdatomic.hをインライン展開している (今回発表にあたって読んでいて、初めて知った)

Slide 44

Slide 44 text

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