Swift Combine: What is AnyCancellable? And how to avoid Memory Leak?
Good at development Or a high-level engineer. Developers who are evaluated in the market in such a statement are thorough in memory management. None of the apps should have any memory leaks. In fact, it is not an exaggeration to say that the development of a developer’s skill is the ability to implement what they want while efficiently managing memory. Combine is convenient but you need to consider memory leak.
Problem Statement
Whenever you are calling publisher.sink()
, it is going to return a particular subscription.
Now, if you don’t really cancel the subscription, then it can start leaking memory or it will start getting the event from the publisher when you don’t need it.
So, maybe you only needed it for once or twice, but if you don’t cancel your subscription, you will keep receiving the vents which are being originated from the publisher.
Cancel
// Publisher
let publisher = CurrentValueSubject<String, Never>("")
// Subscription
let subscription: AnyCancellable = publisher.sink { value in
print(value)
}
publisher.send("hi") // hi
publisher.send("hello world") // hello world
subscription.cancel()
publisher.send("Change Value!!") // Doesn't work// Subscription
let subscription = publisher.sink { value in
print(value)
}
subscription.cancel()
The simplest way is to store your subscription to a variable. By doing this, you can use subscription.cancel()
method. It cancels the subscription.
Set your subscription to nil
// Publisher
let publisher = CurrentValueSubject<String, Never>("")
// Subscriptoin
var subscription: AnyCancellable? = publisher.sink { value in
print(value)
}
publisher.send("hi") // hi
publisher.send("hello world") // hello world
// Cancel Subscription
subscription = nil
publisher.send("Change Value!!") // Doesn't work
// Cancel Subscription
subscription = nil
You can cancel your subscription just by making it optional and setting its value to nil.
What is AnyCancellable
?
var subscription: AnyCancellable
Your guess is right. AnyCancellable is a data type that can hold any subscription
. Nothing more to explain.
Why usually declaring AnyCancellable on Global Scope?
import UIKit
import Combine
class ViewController: UIViewController {
// Publisher
let publisher = CurrentValueSubject<String, Never>("")
var subscription: AnyCancellable?
override func viewDidLoad() {
super.viewDidLoad()
// Subscriptoin
subscription = publisher.sink { value in
print(value)
}
publisher.send("hi") // hi
publisher.send("hello world") // hello world
subscription = nil
publisher.send("Change Value!!") // Doesn't work
}
}
class ViewController: UIViewController {
var subscription: AnyCancellable?
override func viewDidLoad() {
super.viewDidLoad()
...
}
}
You probably have seen this pattern multiple times. It’s just simple. This is because when function execution is once finished, it’s gone from memory forever. That’s why you need to declare AnyCancellable or your subscriptions in your global scope.
Save Multiple Subscriptions
import UIKit
import Combine
class ViewController: UIViewController {
var subscriptions: Set<AnyCancellable> = []
// Publishers
let publisher = CurrentValueSubject<String, Never>("")
let publisher2 = CurrentValueSubject<String, Never>("")
override func viewDidLoad() {
super.viewDidLoad()
// Subscription
publisher.sink { value in
print(value)
}
.store(in: &subscriptions)
// Subscription2
publisher2.sink { value in
print(value)
}
.store(in: &subscriptions)
publisher.send("hi") // hi
publisher.send("hello world") // hello world
subscriptions.removeAll()
publisher.send("Change Value!!") // Doesn't work
}
}
.store(in: &subscriptions)
subscriptions.removeAll()
In order to save your subscriptions, you need to use this method .store(in: &set)
.
To cancel all the subscriptions, just call subscriptions.removeAll()
.
Conclusion
- Save the subscription to
AnyCancellable
variable and callcancel()
- When you have multiple publishers, remove them from your
Set<AnyCancellable>