Disposable.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. //
  2. // Disposable.swift
  3. // ReactiveSwift
  4. //
  5. // Created by Justin Spahr-Summers on 2014-06-02.
  6. // Copyright (c) 2014 GitHub. All rights reserved.
  7. //
  8. /// Represents something that can be “disposed”, usually associated with freeing
  9. /// resources or canceling work.
  10. public protocol Disposable: class {
  11. /// Whether this disposable has been disposed already.
  12. var isDisposed: Bool { get }
  13. /// Disposing of the resources represented by `self`. If `self` has already
  14. /// been disposed of, it does nothing.
  15. ///
  16. /// - note: Implementations must issue a memory barrier.
  17. func dispose()
  18. }
  19. /// Represents the state of a disposable.
  20. private enum DisposableState: Int32 {
  21. /// The disposable is active.
  22. case active
  23. /// The disposable has been disposed.
  24. case disposed
  25. }
  26. extension AtomicStateProtocol where State == DisposableState {
  27. /// Try to transit from `active` to `disposed`.
  28. ///
  29. /// - returns:
  30. /// `true` if the transition succeeds. `false` otherwise.
  31. @inline(__always)
  32. fileprivate func tryDispose() -> Bool {
  33. return tryTransiting(from: .active, to: .disposed)
  34. }
  35. }
  36. /// A type-erased disposable that forwards operations to an underlying disposable.
  37. public final class AnyDisposable: Disposable {
  38. private let disposable: Disposable
  39. public var isDisposed: Bool {
  40. return disposable.isDisposed
  41. }
  42. public init(_ disposable: Disposable) {
  43. self.disposable = disposable
  44. }
  45. public func dispose() {
  46. disposable.dispose()
  47. }
  48. }
  49. /// A disposable that only flips `isDisposed` upon disposal, and performs no other
  50. /// work.
  51. public final class SimpleDisposable: Disposable {
  52. private var state = UnsafeAtomicState(DisposableState.active)
  53. public var isDisposed: Bool {
  54. return state.is(.disposed)
  55. }
  56. public init() {}
  57. public func dispose() {
  58. _ = state.tryDispose()
  59. }
  60. deinit {
  61. state.deinitialize()
  62. }
  63. }
  64. /// A disposable that will run an action upon disposal.
  65. public final class ActionDisposable: Disposable {
  66. private var action: (() -> Void)?
  67. private var state: UnsafeAtomicState<DisposableState>
  68. public var isDisposed: Bool {
  69. return state.is(.disposed)
  70. }
  71. /// Initialize the disposable to run the given action upon disposal.
  72. ///
  73. /// - parameters:
  74. /// - action: A closure to run when calling `dispose()`.
  75. public init(action: @escaping () -> Void) {
  76. self.action = action
  77. self.state = UnsafeAtomicState(DisposableState.active)
  78. }
  79. public func dispose() {
  80. if state.tryDispose() {
  81. action?()
  82. action = nil
  83. }
  84. }
  85. deinit {
  86. state.deinitialize()
  87. }
  88. }
  89. /// A disposable that will dispose of any number of other disposables.
  90. public final class CompositeDisposable: Disposable {
  91. private let disposables: Atomic<Bag<Disposable>?>
  92. private var state: UnsafeAtomicState<DisposableState>
  93. /// Represents a handle to a disposable previously added to a
  94. /// CompositeDisposable.
  95. public final class DisposableHandle {
  96. private var state: UnsafeAtomicState<DisposableState>
  97. private var bagToken: RemovalToken?
  98. private weak var disposable: CompositeDisposable?
  99. fileprivate static let empty = DisposableHandle()
  100. fileprivate init() {
  101. self.state = UnsafeAtomicState(.disposed)
  102. self.bagToken = nil
  103. }
  104. deinit {
  105. state.deinitialize()
  106. }
  107. fileprivate init(bagToken: RemovalToken, disposable: CompositeDisposable) {
  108. self.state = UnsafeAtomicState(.active)
  109. self.bagToken = bagToken
  110. self.disposable = disposable
  111. }
  112. /// Remove the pointed-to disposable from its `CompositeDisposable`.
  113. ///
  114. /// - note: This is useful to minimize memory growth, by removing
  115. /// disposables that are no longer needed.
  116. public func remove() {
  117. if state.tryDispose(), let token = bagToken {
  118. _ = disposable?.disposables.modify {
  119. $0?.remove(using: token)
  120. }
  121. bagToken = nil
  122. disposable = nil
  123. }
  124. }
  125. }
  126. public var isDisposed: Bool {
  127. return state.is(.disposed)
  128. }
  129. /// Initialize a `CompositeDisposable` containing the given sequence of
  130. /// disposables.
  131. ///
  132. /// - parameters:
  133. /// - disposables: A collection of objects conforming to the `Disposable`
  134. /// protocol
  135. public init<S: Sequence>(_ disposables: S)
  136. where S.Iterator.Element == Disposable
  137. {
  138. var bag: Bag<Disposable> = Bag()
  139. for disposable in disposables {
  140. bag.insert(disposable)
  141. }
  142. self.disposables = Atomic(bag)
  143. self.state = UnsafeAtomicState(DisposableState.active)
  144. }
  145. /// Initialize a `CompositeDisposable` containing the given sequence of
  146. /// disposables.
  147. ///
  148. /// - parameters:
  149. /// - disposables: A collection of objects conforming to the `Disposable`
  150. /// protocol
  151. public convenience init<S: Sequence>(_ disposables: S)
  152. where S.Iterator.Element == Disposable?
  153. {
  154. self.init(disposables.flatMap { $0 })
  155. }
  156. /// Initializes an empty `CompositeDisposable`.
  157. public convenience init() {
  158. self.init([Disposable]())
  159. }
  160. public func dispose() {
  161. if state.tryDispose() {
  162. if let ds = disposables.swap(nil) {
  163. for d in ds {
  164. d.dispose()
  165. }
  166. }
  167. }
  168. }
  169. /// Add the given disposable to the list, then return a handle which can
  170. /// be used to opaquely remove the disposable later (if desired).
  171. ///
  172. /// - parameters:
  173. /// - d: Optional disposable.
  174. ///
  175. /// - returns: An instance of `DisposableHandle` that can be used to
  176. /// opaquely remove the disposable later (if desired).
  177. @discardableResult
  178. public func add(_ d: Disposable?) -> DisposableHandle {
  179. guard let d = d else {
  180. return DisposableHandle.empty
  181. }
  182. let handle: DisposableHandle? = disposables.modify {
  183. return ($0?.insert(d)).map { DisposableHandle(bagToken: $0, disposable: self) }
  184. }
  185. if let handle = handle {
  186. return handle
  187. } else {
  188. d.dispose()
  189. return DisposableHandle.empty
  190. }
  191. }
  192. /// Add an ActionDisposable to the list.
  193. ///
  194. /// - parameters:
  195. /// - action: A closure that will be invoked when `dispose()` is called.
  196. ///
  197. /// - returns: An instance of `DisposableHandle` that can be used to
  198. /// opaquely remove the disposable later (if desired).
  199. @discardableResult
  200. public func add(_ action: @escaping () -> Void) -> DisposableHandle {
  201. return add(ActionDisposable(action: action))
  202. }
  203. deinit {
  204. state.deinitialize()
  205. }
  206. }
  207. /// A disposable that, upon deinitialization, will automatically dispose of
  208. /// its inner disposable.
  209. public final class ScopedDisposable<Inner: Disposable>: Disposable {
  210. /// The disposable which will be disposed when the ScopedDisposable
  211. /// deinitializes.
  212. public let inner: Inner
  213. public var isDisposed: Bool {
  214. return inner.isDisposed
  215. }
  216. /// Initialize the receiver to dispose of the argument upon
  217. /// deinitialization.
  218. ///
  219. /// - parameters:
  220. /// - disposable: A disposable to dispose of when deinitializing.
  221. public init(_ disposable: Inner) {
  222. inner = disposable
  223. }
  224. deinit {
  225. dispose()
  226. }
  227. public func dispose() {
  228. return inner.dispose()
  229. }
  230. }
  231. extension ScopedDisposable where Inner: AnyDisposable {
  232. /// Initialize the receiver to dispose of the argument upon
  233. /// deinitialization.
  234. ///
  235. /// - parameters:
  236. /// - disposable: A disposable to dispose of when deinitializing, which
  237. /// will be wrapped in an `AnyDisposable`.
  238. public convenience init(_ disposable: Disposable) {
  239. self.init(Inner(disposable))
  240. }
  241. }
  242. /// A disposable that disposes of its wrapped disposable, and allows its
  243. /// wrapped disposable to be replaced.
  244. public final class SerialDisposable: Disposable {
  245. private let _inner: Atomic<Disposable?>
  246. private var state: UnsafeAtomicState<DisposableState>
  247. public var isDisposed: Bool {
  248. return state.is(.disposed)
  249. }
  250. /// The current inner disposable to dispose of.
  251. ///
  252. /// Whenever this property is set (even to the same value!), the previous
  253. /// disposable is automatically disposed.
  254. public var inner: Disposable? {
  255. get {
  256. return _inner.value
  257. }
  258. set(d) {
  259. _inner.swap(d)?.dispose()
  260. if let d = d, isDisposed {
  261. d.dispose()
  262. }
  263. }
  264. }
  265. /// Initializes the receiver to dispose of the argument when the
  266. /// SerialDisposable is disposed.
  267. ///
  268. /// - parameters:
  269. /// - disposable: Optional disposable.
  270. public init(_ disposable: Disposable? = nil) {
  271. self._inner = Atomic(disposable)
  272. self.state = UnsafeAtomicState(DisposableState.active)
  273. }
  274. public func dispose() {
  275. if state.tryDispose() {
  276. _inner.swap(nil)?.dispose()
  277. }
  278. }
  279. deinit {
  280. state.deinitialize()
  281. }
  282. }
  283. /// Adds the right-hand-side disposable to the left-hand-side
  284. /// `CompositeDisposable`.
  285. ///
  286. /// ````
  287. /// disposable += producer
  288. /// .filter { ... }
  289. /// .map { ... }
  290. /// .start(observer)
  291. /// ````
  292. ///
  293. /// - parameters:
  294. /// - lhs: Disposable to add to.
  295. /// - rhs: Disposable to add.
  296. ///
  297. /// - returns: An instance of `DisposableHandle` that can be used to opaquely
  298. /// remove the disposable later (if desired).
  299. @discardableResult
  300. public func +=(lhs: CompositeDisposable, rhs: Disposable?) -> CompositeDisposable.DisposableHandle {
  301. return lhs.add(rhs)
  302. }
  303. /// Adds the right-hand-side `ActionDisposable` to the left-hand-side
  304. /// `CompositeDisposable`.
  305. ///
  306. /// ````
  307. /// disposable += { ... }
  308. /// ````
  309. ///
  310. /// - parameters:
  311. /// - lhs: Disposable to add to.
  312. /// - rhs: Closure to add as a disposable.
  313. ///
  314. /// - returns: An instance of `DisposableHandle` that can be used to opaquely
  315. /// remove the disposable later (if desired).
  316. @discardableResult
  317. public func +=(lhs: CompositeDisposable, rhs: @escaping () -> ()) -> CompositeDisposable.DisposableHandle {
  318. return lhs.add(rhs)
  319. }
  320. /// Adds the right-hand-side disposable to the left-hand-side
  321. /// `ScopedDisposable<CompositeDisposable>`.
  322. ///
  323. /// ````
  324. /// disposable += { ... }
  325. /// ````
  326. ///
  327. /// - parameters:
  328. /// - lhs: Disposable to add to.
  329. /// - rhs: Disposable to add.
  330. ///
  331. /// - returns: An instance of `DisposableHandle` that can be used to opaquely
  332. /// remove the disposable later (if desired).
  333. @discardableResult
  334. public func +=(lhs: ScopedDisposable<CompositeDisposable>, rhs: Disposable?) -> CompositeDisposable.DisposableHandle {
  335. return lhs.inner.add(rhs)
  336. }
  337. /// Adds the right-hand-side disposable to the left-hand-side
  338. /// `ScopedDisposable<CompositeDisposable>`.
  339. ///
  340. /// ````
  341. /// disposable += { ... }
  342. /// ````
  343. ///
  344. /// - parameters:
  345. /// - lhs: Disposable to add to.
  346. /// - rhs: Closure to add as a disposable.
  347. ///
  348. /// - returns: An instance of `DisposableHandle` that can be used to opaquely
  349. /// remove the disposable later (if desired).
  350. @discardableResult
  351. public func +=(lhs: ScopedDisposable<CompositeDisposable>, rhs: @escaping () -> ()) -> CompositeDisposable.DisposableHandle {
  352. return lhs.inner.add(rhs)
  353. }