Swift 的 Combine 框架 CombineLatest 的问题

Issues with Swift's Combine framework CombineLatest

我浏览了 "Introducing Combine" 的 WWDC 视频,据说每当更新发布者值时,就会调用和更新 CombineLatest。但是我创建的代码片段运行起来很奇怪。

class Mango {
    var enableButton = false
    @Published var userName = "admin"
    @Published var password = "poweruser"
    @Published var passwordAgain = "poweruser"
    var validatePassword: AnyCancellable {
        Publishers.CombineLatest($password, $passwordAgain).map { (password, reenterpass) -> String? in
            print("Is Password Same to \(password)? :", password == reenterpass)
            guard password == reenterpass else { return nil }
            return password
        }.eraseToAnyPublisher()
            .map { (str) -> Bool in
            print("In Map", str != nil)
            guard str != nil else { return false }
            return true
        }.assign(to: \.enableButton, on: self)
    }

    init() {
        validatePassword
    }

    func checkSub() {
        print("1. Is password same? ->",enableButton)
        password = "nopoweruser"
        print("2. Is password same? ->",enableButton)
    }
}

当我初始化并调用函数 checkSub() 时,发布者 'password' 已更新,CombineLatest 不会被调用。为什么它表现异常?

输入:

let mango = Mango()<br>
mango.checkSub()

输出:

Is Password Same to poweruser? : true  
In Map true  
1. Is password same? -> true  
2. Is password same? -> true

看来问题出在内存管理上。 validatePassword 可取消是自动释放的,这意味着订阅在您创建后立即完成,因为您没有保留它。使用 lazy var 使其成为 属性 而不是计算 属性,它应该可以正常工作。

lazy var validatePassword: AnyCancellable = {
    Publishers.CombineLatest($password, $passwordAgain).map { (password, reenterpass) -> String? in
        print("Is Password Same to \(password)? :", password == reenterpass)
        guard password == reenterpass else { return nil }
        return password
    }.eraseToAnyPublisher()
        .map { (str) -> Bool in
        print("In Map", str != nil)
        guard str != nil else { return false }
        return true
    }.assign(to: \.enableButton, on: self)
}()

使用 lazy,您将保留仅在释放对象后才释放的可取消对象。所以,这应该可以正常工作。