Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
RxSwiftのDisposable達をご紹介
Search
Hideki Matsuoka
March 06, 2019
Programming
2.7k
6
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
RxSwiftのDisposable達をご紹介
CA.swift #8 にて発表した資料です
https://cyberagent.connpass.com/event/120746/
Hideki Matsuoka
March 06, 2019
More Decks by Hideki Matsuoka
See All by Hideki Matsuoka
iOSDC2019 多言語対応と戦う2019年版
matsuokah
6
12k
Live Streaming with Screen Recording
matsuokah
3
4.7k
Other Decks in Programming
See All in Programming
AIに既存システムを理解させる技術 ~レガシーを見捨てないハーネスエンジニアリング入門~
ochtum
0
170
Webエンジニアなのにブラウザの仕組みがわからないので、Pythonで自作してみた
tatsuki12
4
1.1k
Claude Code全社展開のためにやったことn選~プラグイン302個・コミッター271人を支えるために~
kenchan
5
1.8k
仕様駆動開発へのトライを機に チームに適合する手法を模索し続けている話
freee
PRO
0
650
書籍「プロフェッショナルAI駆動開発」紹介スライド
juntaromatsumoto
0
780
言葉の格闘技のススメ~紙とペンと言葉から始める、キャリアの描き方~
progresscicada
2
190
属人化した知識を、 AIが辿れる地図にする
pkshadeck
PRO
1
220
React本体のコードリーディング
high_g_engineer
1
160
Pythonの実行はどこまで賢くなったのか? CPythonとPyPyから見る最適化のしくみ
curekoshimizu
3
2.3k
コンパウンドプロダクト開発のためのローカルプロセスマネージャー再発明 #layerxgo
izumin5210
0
460
「寝てても仕事が進む」Claude Codeで組む第二の脳
tomoyafujita2016
0
370
進化を続けるGo toolsの現在地 / The Current State of Ever-Evolving Go Tools
hond0413
0
270
Featured
See All Featured
Abbi's Birthday
coloredviolet
3
9.6k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.5k
Unsuck your backbone
ammeep
672
58k
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
380
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
1.1k
Designing Experiences People Love
moore
143
24k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Un-Boring Meetings
codingconduct
0
400
Practical Orchestrator
shlominoach
191
12k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.6k
Between Models and Reality
mayunak
4
410
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
180
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をインライン展開している (今回発表にあたって読んでいて、初めて知った)
ご静聴ありがとうございました