Reputation: 60251
When using Combine as below
var cancellables: [AnyCancellable] = []
func loadItems(tuple : (name : String, imageURL : URL)) {
URLSession.shared.dataTaskPublisher(for: tuple.imageURL)
.sink(
receiveCompletion: {
completion in
switch completion {
case .finished:
break
case .failure( _):
return
}},
receiveValue: { data, _ in DispatchQueue.main.async { [weak self] in self?.displayFlag(data: data, title: tuple.name) } })
.store(in: &cancellables)
}
We don't need to call cancel
in the deinit
as below
deinit {
cancellables.forEach {
$0.cancel()
}
}
Given that in https://developer.apple.com/documentation/combine/anycancellable, it is stated:
An AnyCancellable instance automatically calls cancel() when deinitialized.
Given we don't need to release during deinit, can the Combine be used in struct
instead of class
?
Upvotes: 5
Views: 3622
Reputation: 5961
To answer your question directly, AnyCancellable
does not rely on being stored in a class in order to cancel itself. Like any ref-counted object, it can be stored in a struct just fine, and it will be properly de-initialized and thus cancelled when there are no more references to it.
That said, you are correct to be suspicious here. You probably don't want to store an AnyCancellable
in a struct the way you are doing it here. For starters, you would have to mark your loadItems
function as mutating
to even get it to compile, because storing the AnyCancellable
means mutating the cancellables
array.
Typically, if you're storing an AnyCancellable
then you are associating that operation with something that has true identity, and thus is better represented as a class. You are basically saying "cancel this operation when this instance goes away". For example, if you're downloading an image to display in a UIViewController
, you probably want to cancel that download if the UIViewController
goes away because the user dismissed it; that is to say, the download operation is associated with a particular instance of UIViewController
.
Since structs have value semantics, it is almost conceptually incoherent to have an AnyCancellable
associated with an "instance" of a struct. Structs don't have instances, they just have values. When you pass a struct as an argument to a function, it creates a copy. That means if the function called loadItems
then only the function's own copy of the struct value would store the AnyCancellable
, and the operation would be immediately cancelled when the function returns because your original copy of the value is not storing the AnyCancellable
.
Upvotes: 10
Reputation: 4585
You don't need deinit
and don't need to call
cancellables.forEach {
$0.cancel()
}
I agree it's quite confusing that AnyCancellable
have method cancel
, that actually you don't need to call.
Publishers are automatically cancelled, when cancellables got disposed.
That is why you receive nothing if forget to store them somewhere.
Upvotes: 0