使用 Apple 的新组合框架时如何防止强引用循环(.assign 导致问题)

Qua*_*ntm 23 swift combine

我不太明白如何在类中正确存储订阅者,以便它们持续存在但不会阻止对象被取消初始化。这是对象不会取消初始化的示例:

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

Run Code Online (Sandbox Code Playgroud)

当我assign用 a替换sink(在其中我正确声明[weak self])时,它实际上确实正确地进行了 deinit(可能是因为assign访问self的方式会导致问题)。

例如,如何在使用时防止强引用循环.assign

谢谢

use*_*734 21

您可以将 .asign(to:) 替换为 sink,其中 [weak self] 在其闭包中会制动内存循环。在 Playground 中尝试看看有什么不同

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
Run Code Online (Sandbox Code Playgroud)

它打印

Hello World!
Goodby, World!
__lldb_expr_4.Bar: deinit
Run Code Online (Sandbox Code Playgroud)

所以我们没有内存泄漏!

终于在 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
    }
  }
}
Run Code Online (Sandbox Code Playgroud)


ger*_*bil 6

怎么样:

class Test {
    @Published var name: String = ""

deinit {
    print("deinit")
}

init(publisher: CurrentValueSubject<String, Never>) {
    publisher.assign(to: &$name)
}
Run Code Online (Sandbox Code Playgroud)

}

此版本的assign操作在内部管理内存(不返回AnyCancellable),因为它与对象一起消亡。请注意,您需要将您的财产转换为@Published.