
13 September 2017
Dive in Disposable Mechanisms in RxSwift
Hey everyone! As a Reactive Lover, I’m going to continue a series of articles dedicated to RxSwift. In my first reactive post, I reasoned about some improvements of flatMap
function (you can check it here) and today I want to extend my knowledge of RxSwift by exploring all Rx Disposables.
This article will explore all Disposables that are available in Rx with short explanations and usage examples.
So, let’s start! 🚀
1. Disposable.Empty
This can be useful whenever you need to fulfil an interface requirement and don’t have any resource management that needs to take place.
Under the hood it uses the private class — NopDisposable (Nop = No Operation)
static public func create() -> Disposable {
return NopDisposable.noOp
}
let disposable = Disposables.create() // useful when disposable mechanism is not necessary
disposable.dispose()
2. Disposable.Create (action)
The second static method provides a disposable with action that is called when the Dispose method is invoked. It’s handy to use this method with server requests — you can control the lifetime of request with dispose block.
In this cases we can see another private class — AnonymousDisposable. If somebody says something about AnonymousDisposable, don’t be afraid — it’s just a Disposable with some dispose action :)
public static func create(with dispose: @escaping () -> ()) -> Cancelable {
return AnonymousDisposable(disposeAction: dispose)
}
let observable = Observable<Void>.create { observer -> Disposable in
// run request
observer.onCompleted()
return Disposables.create {
// request.cancel()
print("disposed ☠️")
}
}
let disposable = observable.subscribe()
⚠️ I want to note that in the previous one, create.empty function returns the Disposable
rather than Cancelable
object like in this AnonymousDisposable
. The differences between them are just in the ability to check the disposed state — isDisposed
.
/// Respresents a disposable resource.
public protocol Disposable {
func dispose()
}
/// Represents disposable resource with state tracking.
public protocol Cancelable: Disposable {
var isDisposed: Bool { get }
}
3. BooleanDisposable
This class simply has the Dispose method and a read-only property isDisposed
that is false when the class is created, and is set to true when the Dispose method is invoked.
let disposable = BooleanDisposable()
print("isDisposed = \(disposable.isDisposed)")
print("dispose")
disposable.dispose()
print("isDisposed = \(disposable.isDisposed)")
// log:
// isDisposed = false
// dispose
// isDisposed = true
4. CompositeDisposable
Represents a group of disposable resources that are disposed together. Calling Dispose on the CompositeDisposable will call dispose on each of these resources in the order they were provided.
let disposable1 = Disposables.create { print("disposed-1 ☠️") }
let disposable2 = Disposables.create { print("disposed-2 ☠️") }
let disposable3 = Disposables.create { print("disposed-3 ☠️") }
let disposable = CompositeDisposable(disposables: [disposable1, disposable2, disposable3])
print("dispose")
disposable.dispose()
// log:
// dispose
// disposed-1 ☠️
// disposed-2 ☠️
// disposed-3 ☠️
There’s one more way of creating a group of disposables using the private BinaryDisposable
class, but not directly, via the static function of Disposables:
public static func create(_ disposable1: Disposable, _ disposable2: Disposable) -> Cancelable {
return BinaryDisposable(disposable1, disposable2)
}
Additionally, the CompositeDisposable
class allows you to add and remove resources. After the CompositeDisposable
has been disposed of, any further resources that are added, will be disposed immediately.
let disposable1 = Disposables.create { print("disposed-1 ☠️") }
let disposable2 = Disposables.create { print("disposed-2 ☠️") }
let disposable3 = Disposables.create { print("disposed-3 ☠️") }
let disposable = CompositeDisposable(disposable1, disposable2)
print("dispose")
disposable.dispose()
print("insert disposable3")
disposable.insert(disposable3) // It'll be disposed immediately, because we called `dispose` in the parent disposable above.
// log:
// dispose
// disposed-1 ☠️
// disposed-2 ☠️
// insert disposable3
// disposed-3 ☠️
Any item that is removed from CompositeDisposable
is also disposed of, regardless of whether the main disposable itself has been disposed of.
let disposable1 = Disposables.create { print("disposed-1 ☠️") }
let disposable2 = Disposables.create { print("disposed-2 ☠️") }
let disposable3 = Disposables.create { print("disposed-3 ☠️") }
let disposable = CompositeDisposable(disposable1, disposable2)
print("insert disposable3")
let key3 = disposable.insert(disposable3)
if let key = key3 {
print("remove disposable3")
disposable.remove(for: key)
}
print("dispose")
disposable.dispose()
// log:
// insert disposable3
// remove disposable3
// disposed-3 ☠️
// dispose
// disposed-1 ☠️
// disposed-2 ☠️
5. RefCountDisposable
Represents a disposable resource that only disposes its underlying disposable resource when all dependent disposable objects have been disposed.
let disposable1 = Disposables.create { print("disposed-1 ☠️") }
let refCountDisposable = RefCountDisposable(disposable: disposable1)
let holdDisposable1 = refCountDisposable.retain()
let holdDisposable2 = refCountDisposable.retain()
print("main dispose")
refCountDisposable.dispose()
print("hold1 dispose")
holdDisposable1.dispose()
print("hold1 dispose")
holdDisposable2.dispose()
// log:
// main dispose
// hold1 dispose
// hold2 dispose
// disposed-1 ☠️
6. ScheduledDisposable
Represents a disposable resource whose disposal invocation will be scheduled on the specified scheduler.
let queue = DispatchQueue.global()
let scheduler = ConcurrentDispatchQueueScheduler(queue: queue)
let observable = Observable<Void>.create { observer in
observer.onCompleted()
return ScheduledDisposable(scheduler: scheduler, disposable: Disposables.create {
print("thread - \(Thread.current)")
})
}
observable.subscribe()
// log:
// thread - <NSThread: 0x7f9631d62e60>{number = 3, name = (null)}
the thread you see is not a main here.
7. SerialDisposable
Represents a disposable resource whose underlying disposable resource can be replaced by another disposable resource, causing automatic disposal of the previous underlying disposable resource.
let disposable1 = Disposables.create { print("disposed-1 ☠️") }
let disposable2 = Disposables.create { print("disposed-2 ☠️") }
let serialDisposable = SerialDisposable()
print("set disposable1")
serialDisposable.disposable = disposable1
print("set disposable2")
serialDisposable.disposable = disposable2
print("dispose")
serialDisposable.dispose()
// log:
// set disposable1
// set disposable2
// disposed-1 ☠️
// dispose
// disposed-2 ☠️
8. SingleAssignmentDisposable
Represents a disposable resource which only allows a single assignment of its underlying disposable resource.
If an underlying disposable resource has already been set, future attempts to set the underlying disposable resource will throw an exception.
let disposable1 = Disposables.create { print("disposed-1 ☠️") }
let disposable2 = Disposables.create { print("disposed-2 ☠️") }
let singleAssignmentDisposable = SingleAssignmentDisposable()
print("set disposable1")
singleAssignmentDisposable.setDisposable(disposable1)
print("set disposable2")
singleAssignmentDisposable.setDisposable(disposable2) // ⚠️⚠️⚠️ fatal error
print("dispose")
singleAssignmentDisposable.dispose()
// log:
// set disposable1
// set disposable2
// ⚠️⚠️⚠️ fatal error
Conclusion
Finally I want to say that there’re so many different disposables, but it’s hard to find a really good usage for them IMO 😭. Please, leave a comment if you use some of the following disposable approaches and it helps you a lot.
If you want to find out more about these disposables, check my playground.
Thanks for reading! I hope you’ve enjoyed my article!
© 2024 Nikita Ermolenko. Some rights reserved.