Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up
for free
RxSwiftのDisposable達をご紹介
Hideki Matsuoka
March 06, 2019
Programming
5
950
RxSwiftのDisposable達をご紹介
CA.swift #8 にて発表した資料です
https://cyberagent.connpass.com/event/120746/
Hideki Matsuoka
March 06, 2019
Tweet
Share
More Decks by Hideki Matsuoka
See All by Hideki Matsuoka
matsuokah
6
6.1k
matsuokah
2
1k
Other Decks in Programming
See All in Programming
loleg
0
190
nanimonodemonai
2
1.4k
muttsu_623
0
460
takahi5
0
200
standfm
0
210
fkubota
1
390
hirotokirimaru
1
400
adoranwodo
0
210
manfredsteyer
PRO
0
250
showwin
0
120
akatsukinewgrad
0
140
freekmurze
0
190
Featured
See All Featured
jmmastey
8
530
geeforr
332
29k
hatefulcrawdad
257
17k
danielanewman
200
19k
vanstee
116
4.8k
maltzj
500
36k
malarkey
393
60k
marcelosomers
220
15k
phodgson
87
3.9k
holman
448
130k
philhawksworth
190
17k
denniskardys
220
120k
Transcript
RxSwiftの Disposable達をご紹介 株式会社マッチングエージェント 松岡 秀樹 Twitter: matsuokah_
松岡 秀樹 (Hideki Matsuoka) • 携わったプロダクト アメーバピグ(バックエンド) PiggPARTY(iOS/Android) famchatty(Android) OPENREC.tv(iOS)
Tapple(iOS) • ⼀⾔ 最近、⾃宅にエルゴヒューマン買った。いい椅⼦はいい ぞ! ◀ イマココ
直近の登壇: iOSDC https://speakerdeck.com/matsuokah/live-streaming-with-screen-recording
アジェンダ • Disposableとは • 基本的なDisposableの種類と使い所 • 応⽤的なDisposableの種類と使い所 • おまけ(時間が余ったら)
質問です
Q . RxSwiftを触ったことがない⼈
Observable.create { _ in … } Q . Observable.createしたことある⼈
Observable.create { _ in return Disposables.create() } Q . Disposables.create()したことあるひと
Observable.create { _ in return Disposables.create { } } Q
. Disposables.create { }したことあるひと
let disposable = Disposables.create() let disposable = Disposables.create { }
Q . 違いがわかる⼈
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を使ったことがある⼈
Disposables.create( Disposables.create { print(1) }, Disposables.create { print(2) }, Disposables.create
{ print(3) }, Disposables.create { print(4) }, Disposables.create { print(5) } ).dispose() Q . 実⾏順序がわかる⼈
Disposablesとは
Disposableとは • RxSwiftにおいてストリームのライフサイクルを 管理するためのもの • Disposableチェーンによって上流のdisposeを ⾏う • Disposable内にdispose時の処理を定義するこ とで、キャンセル処理を⾏うことができる
※イメージ
基本的なDisposable
基本的なDisposable • AnonymousDisposable • NopDisposable • BinaryDisposable • CompositeDisposable
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リクエストをキャンセルする
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 エラーを流して、あとは何もしない
let disposable = Disposables.create() let disposable = Disposables.create { }
Q . 違いがわかる⼈ 実⾏結果は変わらないが、Nopを使った⽅がパフォーマンスはいい
BinaryDisposable • Disposableを2つ保持しておき、disposeされた時に保持してい るDisposableを両⽅disposeする • Disposables.create(disposable , disposable )の実体 Binary
StreamA Subscription StreamB Subscription dispose() dispose() dispose()
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
CompositeDisposable • 2つ以上のDisposableを保持しておき、disposeされた時に保持 している全てのDisposableをdisposeする Composition StreamA Subscription StreamB Subscription dispose()
dispose() dispose() ‧‧‧
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
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の順番の担保ができる
Disposables.create( Disposables.create { print(1) }, Disposables.create { print(2) }, Disposables.create
{ print(3) }, Disposables.create { print(4) }, Disposables.create { print(5) } ).dispose() Q . 実⾏順序がわかる⼈
Disposables.create( Disposables.create { print(1) }, Disposables.create { print(2) }, Disposables.create
{ print(3) }, Disposables.create { print(4) }, Disposables.create { print(5) } ).dispose() Q . 実⾏順序がわかる⼈ 答えは という順番で表⽰される
応⽤的なDisposable
応⽤的なDisposable • SerialDisposable • SingleAssignmentDisposable • ScheduledDisposable
SerialDisposable • Disposableを1つだけ保持するDisposableのラッパー • 新たにDisposableがセットされた時、古いDisposableを disposeする
SingleAssignmentDisposable • 1度だけDisposableをセットできるDisposableのラッパー
ScheduledDisposable • 指定されたスケジューラーでdisposeさせるためのDisposable
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
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
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
disposeEverything cancelSchedule disposeSchedule SerialDisposable SingleAssignmentDisposable NopDisposable スケジュール前 スケジュール後
disposeEverything cancelSchedule disposeSchedule SerialDisposable SingleAssignmentDisposable(disposed) NopDisposable disposeEverything scheduledDisposable parentDisposable SerialDisposable
ScheduledDisposable AnyDisposable(上流のSubscription) scheduled スケジュール前 スケジュール後
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が管理対象に差し替えられる
おまけ
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
おまけ - RxAtomic • stdatomic.hのラッパー • リード‧モディファイ‧ライトな操作にアトミック性を持たせ てくれる • 多重でdisposeのアクションが実⾏されることを防ぐ
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
#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
まとめ • DisposableはRxのストリームのライフサイクル管理と後始末に 使われる • Disposableのはオペレータのライフサイクルや機能に応じて使 い分ける • RxAtomicではCのstdatomic.hをインライン展開している (今回発表にあたって読んでいて、初めて知った)
ご静聴ありがとうございました