Je ne comprends pas très bien comment stocker correctement les abonnés dans une classe afin qu'ils persistent mais n'empêchent pas l'objet d'être désinitialisé. Voici un exemple où l'objet ne désinitie pas:
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
Lorsque je remplace le assign
par un sink
(dans lequel je déclare correctement [weak self]
), il se désinitialise correctement (probablement parce que assign
accède à self
d'une manière qui cause des problèmes).
Comment puis-je empêcher de forts cycles de référence lors de l'utilisation de .assign
par exemple?
Merci
vous pouvez remplacer .asign (to :) par un évier où [self faible] dans sa fermeture freine le cycle de mémoire. Essayez-le dans Playground pour voir la différence
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
il imprime
Hello World!
Goodby, World!
__lldb_expr_4.Bar: deinit
nous n'avons donc pas de fuite de mémoire!
enfin sur forums.Swift.org quelqu'un fait un joli petit
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
}
}
}
Je ne sais pas ce que vous avez contre les fermetures mais la solution est de ne pas utiliser self dans l'attribution:
import Combine
import SwiftUI
class NameStore {
var name: String
init() { name = "" }
deinit { print("deinit NameStore") }
}
class Test {
private var nameStore = NameStore()
public var name: String { get { return nameStore.name } }
var subscriber: AnyCancellable? = nil
deinit { print("deinit Test") }
init(publisher: CurrentValueSubject<String, Never>) {
subscriber = publisher.print().assign(to: \NameStore.name, on: nameStore)
}
}
let publisher = CurrentValueSubject<String, Never>("Test")
var test: Test? = Test(publisher: publisher)
struct ContentView : View {
var body: some View {
Button(
action: { test = nil },
label: {Text("test = nil")}
)
}
}
Pour autant que je puisse voir, les références faibles ne sont autorisées que dans les fermetures, ce n'était donc pas la réponse. Mettre la référence dans un autre objet signifiait que les deux pouvaient être libérés.
J'ai ajouté un ContentView car il facilite la lecture et j'ai ajouté une impression au pipeline pour voir ce qui se passait. Le nom calculé n'est probablement pas nécessaire, il l'a simplement fait ressembler à ce que vous aviez. J'ai également supprimé le Set, c'est probablement utile mais je n'ai pas compris quand.
Vous devez supprimer AnyCancellable
stocké de disposeBag
pour libérer l'instance Test
.
import UIKit
import Combine
private var disposeBag: Set<AnyCancellable> = Set()
class Test {
public var name: String = ""
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)
disposeBag.removeAll()
test = nil
ou utilisez facultatif disposeBag
import UIKit
import Combine
class Test {
public var name: String = ""
private var disposeBag: Set<AnyCancellable>? = Set()
deinit {
print("deinit")
}
init(publisher: CurrentValueSubject<String, Never>) {
guard var disposeBag = disposeBag else { return }
publisher.assign(to: \.name, on: self).store(in: &disposeBag)
}
}
let publisher = CurrentValueSubject<String, Never>("Test")
var test: Test? = Test(publisher: publisher)
test = nil