You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何无法在Swift类中初始化AnyPublisher属性?

问题背景

我希望初始化类中的所有属性,让isEmptyPublisher在初始化完成后立即生效,而不是首次访问时才工作。为什么下面的写法无法初始化AnyPublisher<Bool, Never>属性?

class MyClass {
    private let name: String
    @Published private(set) var friends = [String]()

    let isEmptyPublisher: AnyPublisher<Bool, Never>

    init(name: String) {
        self.name = name
        self.isEmptyPublisher = $friends.map(\.isEmpty).eraseToAnyPublisher()
    }
}

编译器抛出以下错误:

'self' used in property access '$friends' before all stored properties are initialized
'self.isEmptyPublisher' not initialized

我只能改用lazy属性的写法,但这不符合我“初始化后立即生效”的需求:

class MyClass {
    private let name: String
    @Published private(set) var friends = [String]()

    private(set) lazy var isEmptyPublisher: AnyPublisher<Bool, Never> = {
        return $friends.map(\.isEmpty).eraseToAnyPublisher()
    }()

    init(name: String) {
        self.name = name
    }
}

错误原因

Swift初始化有两个核心规则:

  • 类的所有存储属性必须在初始化流程结束前完成赋值,保证self处于完全可用状态。
  • 在所有存储属性未初始化完成前,不能访问self(包括通过属性包装器的投影值,比如$friends)。

你遇到的问题根源在$friends:@Published是属性包装器,$friends作为它的投影发布者,本质需要访问self来获取包装器实例。但在第一个初始化代码中,赋值isEmptyPublisher时,self还没完成所有存储属性的初始化(isEmptyPublisher本身还在赋值过程中),所以编译器判定你提前使用了self,抛出错误。

lazy属性之所以能运行,是因为它的初始化逻辑首次访问时才执行,此时self已经完全初始化,能安全访问$friends。但这种写法的问题是,isEmptyPublisher要等到第一次被调用时才会创建并开始监听friends的变化,没法满足你“初始化后立即生效”的需求。


符合需求的解决方案

如果你想让isEmptyPublisher在初始化完成后立即生效,同时满足属性初始化的要求,可以用以下两种方式:

方案1:结合defer和隐式解包可选类型

class MyClass {
    private let name: String
    @Published private(set) var friends = [String]()
    private(set) var isEmptyPublisher: AnyPublisher<Bool, Never>!

    init(name: String) {
        self.name = name
        // defer块会在所有存储属性初始化完成后执行
        defer {
            self.isEmptyPublisher = $friends.map(\.isEmpty).eraseToAnyPublisher()
        }
    }
}

defer的作用是延迟执行代码块,直到当前初始化流程结束(所有存储属性都已赋值)。此时self完全可用,访问$friends不会触发错误,isEmptyPublisher也会在初始化完成后立即被赋值并开始工作。

方案2:保持let类型,合并初始值与变化发布者

如果你希望isEmptyPublisher是不可变的let类型,可以先创建包含初始值的发布者,再合并后续变化:

class MyClass {
    private let name: String
    @Published private(set) var friends = [String]()
    let isEmptyPublisher: AnyPublisher<Bool, Never>

    init(name: String) {
        self.name = name
        // 先获取初始的isEmpty值,创建立即发送的发布者
        let initialValue = friends.isEmpty
        let initialPublisher = Just(initialValue)
        // 用Deferred延迟获取$friends,确保self初始化完成
        let changesPublisher = Deferred { [weak self] in
            self?.$friends.map(\.isEmpty) ?? Empty().eraseToAnyPublisher()
        }
        // 合并初始值和后续变化
        self.isEmptyPublisher = initialPublisher.merge(with: changesPublisher).eraseToAnyPublisher()
    }
}

Deferred会延迟执行闭包直到订阅发生,此时self已经初始化完成,能安全访问$friends。isEmptyPublisher在初始化时就完成赋值,订阅后会先收到初始值,再监听后续的friends变化。


内容的提问来源于stack exchange,提问作者user10711707

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 22:13:24