CatHand Blog

アプリ開発やMac弄り

Combineのassign()で循環参照

SwiftUIの @Published されてる値をUIKit側から監視したいとき等、↓のようなかんじでCombineのassign()を使うことがあります。

class HogeViewController: UIViewController {

    private var cancellables = Set<AnyCancellable>()
    private var hugaValue: Data? {
        didSet {
            update()
        }
    }

    ...

    init() {
        ...

        model.$hugaValue
            .receive(on: DispatchQueue.main)
            .assign(to: \.hugaValue, on: self)
            .store(in: &cancellables)

        ...
    }

    private func update() {
        ...
    }
}

この assing() はselfを強参照するので注意が必要です。↑の例だと HogeViewController が循環参照されて消えなくなります。

forums.swift.org

解決策としては↑にも書いてありますが、↓のような強参照しない関数に置き換えてやるとよいです。

import Combine

extension Publisher where Self.Failure == Never {
    
    public func assignWeak<Root>(to keyPath: ReferenceWritableKeyPath<Root, Self.Output>, on object: Root) -> AnyCancellable where Root: AnyObject {
        sink { [weak object] (value) in
            object?[keyPath: keyPath] = value
        }
    }
}