Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Swift Combine: cancel a Set<AnyCancellable>

If I have stored a cancellable set into a ViewController:

private var bag = Set<AnyCancellable>() 

Which contains multiple subscription.

1 - Should I cancel subscription in deinit? or it does the job automatically?

2 - If so, how can I cancel all the stored subscriptions?

bag.removeAll() is enough? 

or should I iterate through the set and cancel all subscription one by one?

for sub in bag {    sub.cancel() } 

Apple says that the subscription is alive until the stored AnyCancellable is in memory. So I guess that deallocating the cancellables with bag.removeAll() should be enough, isn't it?

like image 347
Andrea Miotto Avatar asked Nov 22 '19 22:11

Andrea Miotto


People also ask

How do I cancel AnyCancellable?

Whenever an AnyCancellable is deallocated, it will call cancel() on itself. This will run the provided closure that I mentioned earlier. It's safe to assume that this closure will ensure that any resources associated with our subscription are torn down.

When using combine sink operator What happens if you don't store the cancellable that gets returned?

Because without storing the AnyCancelable created by the sink, the garbage collector will delete the sink, I must store it. At the same time, I must also clean it up after the sink has completed, otherwise, I will have a memory leak.


1 Answers

On deinit your ViewController will be removed from memory. All of its instance variables will be deallocated.

The docs for Combine > Publisher > assign(to:on:) say:

An AnyCancellable instance. Call cancel() on this instance when you no longer want the publisher to automatically assign the property. Deinitializing this instance will also cancel automatic assignment.

1 - Should I cancel subscription in deinit? or it does the job automatically?

You don't need to, it does the job automatically. When your ViewController gets deallocated, the instance variable bag will also be deallocated. As there is no more reference to your AnyCancellable's, the assignment will end.

2 - If so, how can I cancel all the stored subscriptions?

Not so. But often you might have some subscriptions that you want to start and stop on, say, viewWillAppear/viewDidDissapear, for example. In this case your ViewController is still in memory.

So, in viewDidDissappear, you can do bag.removeAll() as you suspected. This will remove the references and stop the assigning.

Here is some code you can run to see .removeAll() in action:

var bag = Set<AnyCancellable>()  func testRemoveAll() {   Timer.publish(every: 1, on: .main, in: .common).autoconnect()     .sink { print("===== timer: \($0)") }     .store(in: &bag)    Timer.publish(every: 10, on: .main, in: .common).autoconnect()     .sink { _ in self.bag.removeAll() }     .store(in: &bag) } 

The first timer will fire every one second and print out a line. The second timer will fire after 10 seconds and then call bag.removeAll(). Then both timer publishers will be stopped.

https://developer.apple.com/documentation/combine/publisher/3235801-assign

like image 93
Andy Avatar answered Sep 19 '22 13:09

Andy