| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- //
- // Disposable.swift
- // ReactiveSwift
- //
- // Created by Justin Spahr-Summers on 2014-06-02.
- // Copyright (c) 2014 GitHub. All rights reserved.
- //
- /// Represents something that can be “disposed”, usually associated with freeing
- /// resources or canceling work.
- public protocol Disposable: class {
- /// Whether this disposable has been disposed already.
- var isDisposed: Bool { get }
- /// Disposing of the resources represented by `self`. If `self` has already
- /// been disposed of, it does nothing.
- ///
- /// - note: Implementations must issue a memory barrier.
- func dispose()
- }
- /// Represents the state of a disposable.
- private enum DisposableState: Int32 {
- /// The disposable is active.
- case active
- /// The disposable has been disposed.
- case disposed
- }
- extension AtomicStateProtocol where State == DisposableState {
- /// Try to transit from `active` to `disposed`.
- ///
- /// - returns:
- /// `true` if the transition succeeds. `false` otherwise.
- @inline(__always)
- fileprivate func tryDispose() -> Bool {
- return tryTransiting(from: .active, to: .disposed)
- }
- }
- /// A type-erased disposable that forwards operations to an underlying disposable.
- public final class AnyDisposable: Disposable {
- private let disposable: Disposable
- public var isDisposed: Bool {
- return disposable.isDisposed
- }
- public init(_ disposable: Disposable) {
- self.disposable = disposable
- }
- public func dispose() {
- disposable.dispose()
- }
- }
- /// A disposable that only flips `isDisposed` upon disposal, and performs no other
- /// work.
- public final class SimpleDisposable: Disposable {
- private var state = UnsafeAtomicState(DisposableState.active)
- public var isDisposed: Bool {
- return state.is(.disposed)
- }
- public init() {}
- public func dispose() {
- _ = state.tryDispose()
- }
- deinit {
- state.deinitialize()
- }
- }
- /// A disposable that will run an action upon disposal.
- public final class ActionDisposable: Disposable {
- private var action: (() -> Void)?
- private var state: UnsafeAtomicState<DisposableState>
- public var isDisposed: Bool {
- return state.is(.disposed)
- }
- /// Initialize the disposable to run the given action upon disposal.
- ///
- /// - parameters:
- /// - action: A closure to run when calling `dispose()`.
- public init(action: @escaping () -> Void) {
- self.action = action
- self.state = UnsafeAtomicState(DisposableState.active)
- }
- public func dispose() {
- if state.tryDispose() {
- action?()
- action = nil
- }
- }
- deinit {
- state.deinitialize()
- }
- }
- /// A disposable that will dispose of any number of other disposables.
- public final class CompositeDisposable: Disposable {
- private let disposables: Atomic<Bag<Disposable>?>
- private var state: UnsafeAtomicState<DisposableState>
- /// Represents a handle to a disposable previously added to a
- /// CompositeDisposable.
- public final class DisposableHandle {
- private var state: UnsafeAtomicState<DisposableState>
- private var bagToken: RemovalToken?
- private weak var disposable: CompositeDisposable?
- fileprivate static let empty = DisposableHandle()
- fileprivate init() {
- self.state = UnsafeAtomicState(.disposed)
- self.bagToken = nil
- }
- deinit {
- state.deinitialize()
- }
- fileprivate init(bagToken: RemovalToken, disposable: CompositeDisposable) {
- self.state = UnsafeAtomicState(.active)
- self.bagToken = bagToken
- self.disposable = disposable
- }
- /// Remove the pointed-to disposable from its `CompositeDisposable`.
- ///
- /// - note: This is useful to minimize memory growth, by removing
- /// disposables that are no longer needed.
- public func remove() {
- if state.tryDispose(), let token = bagToken {
- _ = disposable?.disposables.modify {
- $0?.remove(using: token)
- }
- bagToken = nil
- disposable = nil
- }
- }
- }
- public var isDisposed: Bool {
- return state.is(.disposed)
- }
- /// Initialize a `CompositeDisposable` containing the given sequence of
- /// disposables.
- ///
- /// - parameters:
- /// - disposables: A collection of objects conforming to the `Disposable`
- /// protocol
- public init<S: Sequence>(_ disposables: S)
- where S.Iterator.Element == Disposable
- {
- var bag: Bag<Disposable> = Bag()
- for disposable in disposables {
- bag.insert(disposable)
- }
- self.disposables = Atomic(bag)
- self.state = UnsafeAtomicState(DisposableState.active)
- }
-
- /// Initialize a `CompositeDisposable` containing the given sequence of
- /// disposables.
- ///
- /// - parameters:
- /// - disposables: A collection of objects conforming to the `Disposable`
- /// protocol
- public convenience init<S: Sequence>(_ disposables: S)
- where S.Iterator.Element == Disposable?
- {
- self.init(disposables.flatMap { $0 })
- }
- /// Initializes an empty `CompositeDisposable`.
- public convenience init() {
- self.init([Disposable]())
- }
- public func dispose() {
- if state.tryDispose() {
- if let ds = disposables.swap(nil) {
- for d in ds {
- d.dispose()
- }
- }
- }
- }
- /// Add the given disposable to the list, then return a handle which can
- /// be used to opaquely remove the disposable later (if desired).
- ///
- /// - parameters:
- /// - d: Optional disposable.
- ///
- /// - returns: An instance of `DisposableHandle` that can be used to
- /// opaquely remove the disposable later (if desired).
- @discardableResult
- public func add(_ d: Disposable?) -> DisposableHandle {
- guard let d = d else {
- return DisposableHandle.empty
- }
- let handle: DisposableHandle? = disposables.modify {
- return ($0?.insert(d)).map { DisposableHandle(bagToken: $0, disposable: self) }
- }
- if let handle = handle {
- return handle
- } else {
- d.dispose()
- return DisposableHandle.empty
- }
- }
- /// Add an ActionDisposable to the list.
- ///
- /// - parameters:
- /// - action: A closure that will be invoked when `dispose()` is called.
- ///
- /// - returns: An instance of `DisposableHandle` that can be used to
- /// opaquely remove the disposable later (if desired).
- @discardableResult
- public func add(_ action: @escaping () -> Void) -> DisposableHandle {
- return add(ActionDisposable(action: action))
- }
- deinit {
- state.deinitialize()
- }
- }
- /// A disposable that, upon deinitialization, will automatically dispose of
- /// its inner disposable.
- public final class ScopedDisposable<Inner: Disposable>: Disposable {
- /// The disposable which will be disposed when the ScopedDisposable
- /// deinitializes.
- public let inner: Inner
- public var isDisposed: Bool {
- return inner.isDisposed
- }
- /// Initialize the receiver to dispose of the argument upon
- /// deinitialization.
- ///
- /// - parameters:
- /// - disposable: A disposable to dispose of when deinitializing.
- public init(_ disposable: Inner) {
- inner = disposable
- }
- deinit {
- dispose()
- }
- public func dispose() {
- return inner.dispose()
- }
- }
- extension ScopedDisposable where Inner: AnyDisposable {
- /// Initialize the receiver to dispose of the argument upon
- /// deinitialization.
- ///
- /// - parameters:
- /// - disposable: A disposable to dispose of when deinitializing, which
- /// will be wrapped in an `AnyDisposable`.
- public convenience init(_ disposable: Disposable) {
- self.init(Inner(disposable))
- }
- }
- /// A disposable that disposes of its wrapped disposable, and allows its
- /// wrapped disposable to be replaced.
- public final class SerialDisposable: Disposable {
- private let _inner: Atomic<Disposable?>
- private var state: UnsafeAtomicState<DisposableState>
- public var isDisposed: Bool {
- return state.is(.disposed)
- }
- /// The current inner disposable to dispose of.
- ///
- /// Whenever this property is set (even to the same value!), the previous
- /// disposable is automatically disposed.
- public var inner: Disposable? {
- get {
- return _inner.value
- }
- set(d) {
- _inner.swap(d)?.dispose()
- if let d = d, isDisposed {
- d.dispose()
- }
- }
- }
- /// Initializes the receiver to dispose of the argument when the
- /// SerialDisposable is disposed.
- ///
- /// - parameters:
- /// - disposable: Optional disposable.
- public init(_ disposable: Disposable? = nil) {
- self._inner = Atomic(disposable)
- self.state = UnsafeAtomicState(DisposableState.active)
- }
- public func dispose() {
- if state.tryDispose() {
- _inner.swap(nil)?.dispose()
- }
- }
- deinit {
- state.deinitialize()
- }
- }
- /// Adds the right-hand-side disposable to the left-hand-side
- /// `CompositeDisposable`.
- ///
- /// ````
- /// disposable += producer
- /// .filter { ... }
- /// .map { ... }
- /// .start(observer)
- /// ````
- ///
- /// - parameters:
- /// - lhs: Disposable to add to.
- /// - rhs: Disposable to add.
- ///
- /// - returns: An instance of `DisposableHandle` that can be used to opaquely
- /// remove the disposable later (if desired).
- @discardableResult
- public func +=(lhs: CompositeDisposable, rhs: Disposable?) -> CompositeDisposable.DisposableHandle {
- return lhs.add(rhs)
- }
- /// Adds the right-hand-side `ActionDisposable` to the left-hand-side
- /// `CompositeDisposable`.
- ///
- /// ````
- /// disposable += { ... }
- /// ````
- ///
- /// - parameters:
- /// - lhs: Disposable to add to.
- /// - rhs: Closure to add as a disposable.
- ///
- /// - returns: An instance of `DisposableHandle` that can be used to opaquely
- /// remove the disposable later (if desired).
- @discardableResult
- public func +=(lhs: CompositeDisposable, rhs: @escaping () -> ()) -> CompositeDisposable.DisposableHandle {
- return lhs.add(rhs)
- }
- /// Adds the right-hand-side disposable to the left-hand-side
- /// `ScopedDisposable<CompositeDisposable>`.
- ///
- /// ````
- /// disposable += { ... }
- /// ````
- ///
- /// - parameters:
- /// - lhs: Disposable to add to.
- /// - rhs: Disposable to add.
- ///
- /// - returns: An instance of `DisposableHandle` that can be used to opaquely
- /// remove the disposable later (if desired).
- @discardableResult
- public func +=(lhs: ScopedDisposable<CompositeDisposable>, rhs: Disposable?) -> CompositeDisposable.DisposableHandle {
- return lhs.inner.add(rhs)
- }
- /// Adds the right-hand-side disposable to the left-hand-side
- /// `ScopedDisposable<CompositeDisposable>`.
- ///
- /// ````
- /// disposable += { ... }
- /// ````
- ///
- /// - parameters:
- /// - lhs: Disposable to add to.
- /// - rhs: Closure to add as a disposable.
- ///
- /// - returns: An instance of `DisposableHandle` that can be used to opaquely
- /// remove the disposable later (if desired).
- @discardableResult
- public func +=(lhs: ScopedDisposable<CompositeDisposable>, rhs: @escaping () -> ()) -> CompositeDisposable.DisposableHandle {
- return lhs.inner.add(rhs)
- }
|