I don't quite understand how to properly store subscribers inside a class so that they persist but don't prevent the object from being deinitialized. Here's an example where the object won't deinit:
import UIKit import Combine class Test { public var name: String = "" private var disposeBag: Set<AnyCancellable> = Set() deinit { print("deinit") } init(publisher: CurrentValueSubject<String, Never>) { publisher.assign(to: \.name, on: self).store(in: &disposeBag) } } let publisher = CurrentValueSubject<String, Never>("Test") var test: Test? = Test(publisher: publisher) test = nil
When I replace the assign
with a sink
(in which I properly declare [weak self]
) it actually does deinit properly (probably because the assign
accesses self
in a way that causes problems).
How can I prevent strong reference cycles when using .assign
for instance?
Thanks
A strong reference cycle happens when 2 instances keep a strong reference to each other. You can accidentally create such a cyclic reference, for example when working with 2-way “links” between objects, or with closures. You can break the cycle by marking a reference as weak, or by setting one of the references to nil.
The Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events. Combine declares publishers to expose values that can change over time, and subscribers to receive those values from the publishers.
you can replace .asign(to:) with sink where [weak self] in its closure brake the memory cycle. Try it in Playground to see the difference
final class Bar: ObservableObject { @Published var input: String = "" @Published var output: String = "" private var subscription: AnyCancellable? init() { subscription = $input .filter { $0.count > 0 } .map { "\($0) World!" } //.assignNoRetain(to: \.output, on: self) .sink { [weak self] (value) in self?.output = value } } deinit { subscription?.cancel() print("\(self): \(#function)") } } // test it!! var bar: Bar? = Bar() let foo = bar?.$output.sink { print($0) } bar?.input = "Hello" bar?.input = "Goodby," bar = nil
it prints
Hello World! Goodby, World! __lldb_expr_4.Bar: deinit
so we don't have the memory leak !
finally at forums.swift.org someone make a nice little
extension Publisher where Self.Failure == Never { public func assignNoRetain<Root>(to keyPath: ReferenceWritableKeyPath<Root, Self.Output>, on object: Root) -> AnyCancellable where Root: AnyObject { sink { [weak object] (value) in object?[keyPath: keyPath] = value } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With