サブスクライバーをクラス内に適切に格納して永続化する方法はよくわかりませんが、オブジェクトが非初期化されるのを防ぎません。次に、オブジェクトが初期化されない例を示します。
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
assign
をsink
に置き換えると([weak self]
を適切に宣言します)、実際には正しく初期化を解除します(おそらくassign
がself
問題を引き起こす方法で)。
たとえば.assign
を使用する場合、どのようにして強い参照サイクルを防ぐことができますか?
ありがとう
.asign(to :)をシンクに置き換えると、クロージャの[weak self]がメモリサイクルをブレーキします。プレイグラウンドで試して、違いを確認してください
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
印刷する
Hello World!
Goodby, World!
__lldb_expr_4.Bar: deinit
メモリリークはありません。
最終的にforums.Swift.orgで誰かが素敵な小さな
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
}
}
}
私はあなたがクロージャーに対して何を持っているのかわかりませんが、解決策は代入で自分自身を使用しないことです:
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")}
)
}
}
私が見る限り、弱い参照はクロージャーでのみ許可されているので、それは答えではありませんでした。参照を別のオブジェクトに入れると、両方が解放される可能性があります。
簡単に操作できるようにContentViewを追加し、何が起こっているのかを確認するためにパイプラインに印刷を追加しました。計算された名前はおそらく必要ではなく、単にあなたが持っていたのと同じように見えただけです。また、セットも削除しました。おそらく便利ですが、まだ機能していません。
保存されているAnyCancellable
をdisposeBag
から削除して、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
またはオプションの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