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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
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導入の「期待外れ」を乗り越える ー 開発フロー改革が目指す、真の組織変革
starfish719
0
4.1k
源内ハンズオン概要編
hideg
0
130
the container ship “Apple Silicon”@WWDC26 Recap -Japan-\(region).swift
shingangan
0
110
AWS CDK を「作」ってみた 〜フルスクラッチで見えた CDK の裏側〜 / aws-cdk-from-scratch
gotok365
3
2.8k
霧の中の代数的エフェクト
funnyycat
1
490
AI時代、エンジニアはどう育つのか -未経験エンジニアの成長を間近で見て考えたこと-
thasu0123
0
220
言葉の格闘技のススメ~紙とペンと言葉から始める、キャリアの描き方~
progresscicada
2
140
AI時代のPHPer生存戦略 ~「言語、もうなんでもよくない?」に本気で向き合う~
vivion
0
300
PHP に部分適用が来るぞ!……ところで何それ?おいしいの? #phpcon / phpcon-2026
shogogg
0
600
「寝てても仕事が進む」Claude Codeで組む第二の脳
tomoyafujita2016
0
310
「人を評価する AI」の設計と実装
ryoyanara
0
180
これって Effect でできたのでは? / TSKaigi Mashup Kansai #2
susisu
0
150
Featured
See All Featured
Practical Orchestrator
shlominoach
191
12k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
630
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
210
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.5k
Making Projects Easy
brettharned
120
6.7k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Skip the Path - Find Your Career Trail
mkilby
1
180
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.4k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
sira's awesome portfolio website redesign presentation
elsirapls
0
320
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6k
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をインライン展開している (今回発表にあたって読んでいて、初めて知った)
ご静聴ありがとうございました