Disposable.swift 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. //
  2. // Disposable.swift
  3. // ReactiveCocoa
  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 disposed: Bool { get }
  13. /// Method for disposing of resources when appropriate.
  14. func dispose()
  15. }
  16. /// A disposable that only flips `disposed` upon disposal, and performs no other
  17. /// work.
  18. public final class SimpleDisposable: Disposable {
  19. private let _disposed = Atomic(false)
  20. public var disposed: Bool {
  21. return _disposed.value
  22. }
  23. public init() {}
  24. public func dispose() {
  25. _disposed.value = true
  26. }
  27. }
  28. /// A disposable that will run an action upon disposal.
  29. public final class ActionDisposable: Disposable {
  30. private let action: Atomic<(() -> Void)?>
  31. public var disposed: Bool {
  32. return action.value == nil
  33. }
  34. /// Initialize the disposable to run the given action upon disposal.
  35. ///
  36. /// - parameters:
  37. /// - action: A closure to run when calling `dispose()`.
  38. public init(action: () -> Void) {
  39. self.action = Atomic(action)
  40. }
  41. public func dispose() {
  42. let oldAction = action.swap(nil)
  43. oldAction?()
  44. }
  45. }
  46. /// A disposable that will dispose of any number of other disposables.
  47. public final class CompositeDisposable: Disposable {
  48. private let disposables: Atomic<Bag<Disposable>?>
  49. /// Represents a handle to a disposable previously added to a
  50. /// CompositeDisposable.
  51. public final class DisposableHandle {
  52. private let bagToken: Atomic<RemovalToken?>
  53. private weak var disposable: CompositeDisposable?
  54. private static let empty = DisposableHandle()
  55. private init() {
  56. self.bagToken = Atomic(nil)
  57. }
  58. private init(bagToken: RemovalToken, disposable: CompositeDisposable) {
  59. self.bagToken = Atomic(bagToken)
  60. self.disposable = disposable
  61. }
  62. /// Remove the pointed-to disposable from its `CompositeDisposable`.
  63. ///
  64. /// - note: This is useful to minimize memory growth, by removing
  65. /// disposables that are no longer needed.
  66. public func remove() {
  67. if let token = bagToken.swap(nil) {
  68. disposable?.disposables.modify { bag in
  69. guard var bag = bag else {
  70. return nil
  71. }
  72. bag.removeValueForToken(token)
  73. return bag
  74. }
  75. }
  76. }
  77. }
  78. public var disposed: Bool {
  79. return disposables.value == nil
  80. }
  81. /// Initialize a `CompositeDisposable` containing the given sequence of
  82. /// disposables.
  83. ///
  84. /// - parameters:
  85. /// - disposables: A collection of objects conforming to the `Disposable`
  86. /// protocol
  87. public init<S: SequenceType where S.Generator.Element == Disposable>(_ disposables: S) {
  88. var bag: Bag<Disposable> = Bag()
  89. for disposable in disposables {
  90. bag.insert(disposable)
  91. }
  92. self.disposables = Atomic(bag)
  93. }
  94. /// Initialize a `CompositeDisposable` containing the given sequence of
  95. /// disposables.
  96. ///
  97. /// - parameters:
  98. /// - disposables: A collection of objects conforming to the `Disposable`
  99. /// protocol
  100. public convenience init<S: SequenceType where S.Generator.Element == Disposable?>(_ disposables: S) {
  101. self.init(disposables.flatMap { $0 })
  102. }
  103. /// Initializes an empty `CompositeDisposable`.
  104. public convenience init() {
  105. self.init([Disposable]())
  106. }
  107. public func dispose() {
  108. if let ds = disposables.swap(nil) {
  109. for d in ds.reverse() {
  110. d.dispose()
  111. }
  112. }
  113. }
  114. /// Add the given disposable to the list, then return a handle which can
  115. /// be used to opaquely remove the disposable later (if desired).
  116. ///
  117. /// - parameters:
  118. /// - d: Optional disposable.
  119. ///
  120. /// - returns: An instance of `DisposableHandle` that can be used to
  121. /// opaquely remove the disposable later (if desired).
  122. public func addDisposable(d: Disposable?) -> DisposableHandle {
  123. guard let d = d else {
  124. return DisposableHandle.empty
  125. }
  126. var handle: DisposableHandle? = nil
  127. disposables.modify { ds in
  128. guard var ds = ds else {
  129. return nil
  130. }
  131. let token = ds.insert(d)
  132. handle = DisposableHandle(bagToken: token, disposable: self)
  133. return ds
  134. }
  135. if let handle = handle {
  136. return handle
  137. } else {
  138. d.dispose()
  139. return DisposableHandle.empty
  140. }
  141. }
  142. /// Add an ActionDisposable to the list.
  143. ///
  144. /// - parameters:
  145. /// - action: A closure that will be invoked when `dispose()` is called.
  146. ///
  147. /// - returns: An instance of `DisposableHandle` that can be used to
  148. /// opaquely remove the disposable later (if desired).
  149. public func addDisposable(action: () -> Void) -> DisposableHandle {
  150. return addDisposable(ActionDisposable(action: action))
  151. }
  152. }
  153. /// A disposable that, upon deinitialization, will automatically dispose of
  154. /// another disposable.
  155. public final class ScopedDisposable: Disposable {
  156. /// The disposable which will be disposed when the ScopedDisposable
  157. /// deinitializes.
  158. public let innerDisposable: Disposable
  159. public var disposed: Bool {
  160. return innerDisposable.disposed
  161. }
  162. /// Initialize the receiver to dispose of the argument upon
  163. /// deinitialization.
  164. ///
  165. /// - parameters:
  166. /// - disposable: A disposable to dispose of when deinitializing.
  167. public init(_ disposable: Disposable) {
  168. innerDisposable = disposable
  169. }
  170. deinit {
  171. dispose()
  172. }
  173. public func dispose() {
  174. innerDisposable.dispose()
  175. }
  176. }
  177. /// A disposable that will optionally dispose of another disposable.
  178. public final class SerialDisposable: Disposable {
  179. private struct State {
  180. var innerDisposable: Disposable? = nil
  181. var disposed = false
  182. }
  183. private let state = Atomic(State())
  184. public var disposed: Bool {
  185. return state.value.disposed
  186. }
  187. /// The inner disposable to dispose of.
  188. ///
  189. /// Whenever this property is set (even to the same value!), the previous
  190. /// disposable is automatically disposed.
  191. public var innerDisposable: Disposable? {
  192. get {
  193. return state.value.innerDisposable
  194. }
  195. set(d) {
  196. let oldState = state.modify { state in
  197. var state = state
  198. state.innerDisposable = d
  199. return state
  200. }
  201. oldState.innerDisposable?.dispose()
  202. if oldState.disposed {
  203. d?.dispose()
  204. }
  205. }
  206. }
  207. /// Initializes the receiver to dispose of the argument when the
  208. /// SerialDisposable is disposed.
  209. ///
  210. /// - parameters:
  211. /// - disposable: Optional disposable.
  212. public init(_ disposable: Disposable? = nil) {
  213. innerDisposable = disposable
  214. }
  215. public func dispose() {
  216. let orig = state.swap(State(innerDisposable: nil, disposed: true))
  217. orig.innerDisposable?.dispose()
  218. }
  219. }
  220. /// Adds the right-hand-side disposable to the left-hand-side
  221. /// `CompositeDisposable`.
  222. ///
  223. /// ````
  224. /// disposable += producer
  225. /// .filter { ... }
  226. /// .map { ... }
  227. /// .start(observer)
  228. /// ````
  229. ///
  230. /// - parameters:
  231. /// - lhs: Disposable to add to.
  232. /// - rhs: Disposable to add.
  233. ///
  234. /// - returns: An instance of `DisposableHandle` that can be used to opaquely
  235. /// remove the disposable later (if desired).
  236. public func +=(lhs: CompositeDisposable, rhs: Disposable?) -> CompositeDisposable.DisposableHandle {
  237. return lhs.addDisposable(rhs)
  238. }
  239. /// Adds the right-hand-side `ActionDisposable` to the left-hand-side
  240. /// `CompositeDisposable`.
  241. ///
  242. /// ````
  243. /// disposable += { ... }
  244. /// ````
  245. ///
  246. /// - parameters:
  247. /// - lhs: Disposable to add to.
  248. /// - rhs: Closure to add as a disposable.
  249. ///
  250. /// - returns: An instance of `DisposableHandle` that can be used to opaquely
  251. /// remove the disposable later (if desired).
  252. public func +=(lhs: CompositeDisposable, rhs: () -> ()) -> CompositeDisposable.DisposableHandle {
  253. return lhs.addDisposable(rhs)
  254. }