Swift Combine how Set<AnyCancellable> works?
Asked Answered
E

1

7

I have ViewModel with disposable Set defined this way

class ViewModel { 


 private var disposables = Set<AnyCancellable>()

 func sync() { 
    repo.syncObjects()
            .handleEvents(receiveCancel: {
                print("Synced objects: CANCELED!")
            })
            .sink(receiveCompletion: { completion in
                switch completion {
                case .failure(let error):
                    print("Synced objects: \(error)")
                case .finished:
                    print("Synced objects: finished")
                }
            }) { objects in
                print("Synced objects: \(objects)")
            }.store(in: &disposables)
 }

  deinit { print("ViewModel deinit") }
}

I am calling sync() in onAppear in SwiftUI view. Then I am fast switching screens and ViewModel referenced from SwiftUI view is deallocated by ARC like deinit is called but subscriptions seems to remain alive and disposable reference does not cancel subscription it fetches data from Network and saved them in Core Data and prints Synced objects: objects, Synced objects: finished. And keeps being alive even when I stop switching screens for several seconds to complete old requests.

Should I manually cancel AnyCancellable? shouldn't it be cancelled automagically?

Extortion answered 19/5, 2020 at 10:46 Comment(1)
Show us how syncObjects is implemented.Lenz
V
9

No, you dont need to cancel any cancellable because this kind of object calls the method cancel when deinitialized. So your code is correct.

Apple's documentation of AnyCancellable:

An AnyCancellable instance automatically calls cancel() when deinitialized

https://developer.apple.com/documentation/combine/anycancellable

Viol answered 19/5, 2020 at 10:54 Comment(4)
but my network request in sync() seems to be alive and stores results in core data in spite of leaving screen. I'va also deinit { print() } called and printed to console so it indicates object i deallocated. But simultanously receiveCancel in subscription is not called. In Instrumtents Allocations this ViewModel seems to be allocated ? there I have 50 persitent 50 transient and 100 total allocations. It is @ObservableObject in SwiftUI View so if view is changed it should be deallocated I thinkHintze
Can you share a little example of your specific situation please? jejejeViol
I found that there are memory leaks. I've also added print statement to init() and deinit() and there are usually 2 allocations and 1 deallocations so I think this is the reason that anycancellables are not cancelled. So I think I need to search for this issue ratherHintze
Yes it is the most probable thing, because if some leak memory keeps the reference to the object the deinitialized of the cancellables are not called :)Viol

© 2022 - 2024 — McMap. All rights reserved.