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

SwiftUI中给@Published属性赋值是否需要先校验与原值不同?

是否需要校验赋值

如果你的视图结构简单、该@Published属性赋值频率较低,冗余刷新的开销极小可以忽略;如果视图包含复杂布局、大量子视图,或是该属性存在高频赋值场景,建议做优化避免不必要的body调用。

差异原因
  • @State 是SwiftUI内置的状态包装器,内部默认实现了等值判断逻辑,仅当存储的值实际发生变更时才会触发视图重绘。
  • @Published 属于Combine框架的属性包装器,默认逻辑为:只要触发属性赋值操作,无论新旧值是否相等,都会立即发送所属ObservableObject的objectWillChange信号,进而通知所有观察该对象的视图刷新,这就是你示例中每次点击都触发body调用的核心原因。
优化方案

方案1:赋值前手动校验值变更

兼容性最好、实现最简单的方案,仅在值实际变化时执行赋值操作即可:

class Object: ObservableObject {
    @Published var state: Bool = false
    
    func setState() {
        guard state != false else { return }
        state = false
    }
}

如果有大量同类型属性需要优化,可以自定义带等值判断的@Published属性包装器,统一避免冗余通知:

@propertyWrapper
struct PublishedNoDuplicate<Value: Equatable> {
    private var value: Value
    private let publisher = PassthroughSubject<Value, Never>()
    
    var wrappedValue: Value {
        get { value }
        set {
            guard newValue != value else { return }
            value = newValue
            publisher.send(newValue)
        }
    }
    
    var projectedValue: AnyPublisher<Value, Never> {
        publisher.eraseToAnyPublisher()
    }
    
    init(wrappedValue: Value) {
        self.value = wrappedValue
    }
}

方案2:视图层用EquatableView做拦截

如果不想修改ObservableObject的代码,可以在视图层做等值判断,SwiftUI会先校验视图是否真的需要重绘:

extension ContentView: Equatable {
    static func == (lhs: ContentView, rhs: ContentView) -> Bool {
        lhs.state == rhs.state && lhs.obj.state == rhs.obj.state
    }
}

使用视图时外层套一层EquatableView即可:

EquatableView(ContentView())

方案3:iOS 17+ 直接使用@Observable宏

iOS 17推出的Observation框架从底层解决了这个问题,它实现了属性级别的精准变更检测,仅当视图实际读取的属性发生真实变更时才会触发刷新,相同值赋值不会产生任何冗余调用:

@Observable
class Object {
    var state: Bool = false
    
    func setState() {
        state = false
    }
}

struct ContentView: View {
    @State var state: Bool = false
    var obj = Object() // 无需再使用@ObservedObject包装

    var body: some View {
        print(Self._printChanges())
        return VStack {
            Text("\(String(state))")
            Button("Tap") {
                self.state = false
                obj.setState()
            }
        }
    }
}

上述代码运行后连续点击按钮不会产生任何刷新日志。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 17:15:05