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

如何通过反射获取Swift ObservableObject的@Published属性?

实现自定义@Observed属性包装器监听@Published属性变化

我需要实现一个类似@ObservedObject的属性包装器@Observed,用来监听遵循ObservableObject协议的类中@Published属性发出的Combine事件,把所有sink处理逻辑封装在Swift包里,不用在主代码库中管理。目前的问题是,如何通过反射识别@Published属性,并订阅这些属性的事件。

我的目标是构建类似SwiftUI的Combine响应式架构,当被包装的ObservableObject的@Published属性变化时,执行模型序列化等操作,而非更新界面。我想搞清楚SwiftUI如何感知子模型更新并触发重绘,认为通过反射在根层级获取子对象的所有属性并订阅,就能实现这类响应式架构。

现有代码示例:

@propertyWrapper
struct Observed<T> where T : ObservableObject {
    var sinks = [AnyCancellable]()
    var wrappedValue: T
    
    init(wrappedValue: T) {
        self.wrappedValue = wrappedValue
        let mirror = Mirror(reflecting: wrappedValue)
        for case let (label?, value) in mirror.children {
            //if `value` is wrapped as an `@Published` property {
            //   grab sink to the property
            //}
        }
    }
}

解决方案代码

import Combine

@propertyWrapper
struct Observed<T> where T : ObservableObject {
    private var sinks = Set<AnyCancellable>()
    var wrappedValue: T
    
    init(wrappedValue: T) {
        self.wrappedValue = wrappedValue
        let mirror = Mirror(reflecting: wrappedValue)
        
        // 遍历所有属性,筛选并订阅@Published属性
        for case let (label?, value) in mirror.children {
            guard let published = value as? AnyPublished else {
                continue
            }
            
            published.publisher
                .sink { [weak self] _ in
                    guard let self = self else { return }
                    self.handlePropertyChange(for: label)
                }
                .store(in: &sinks)
        }
        
        // 订阅对象级别的变更通知(类似SwiftUI的重绘触发逻辑)
        wrappedValue.objectWillChange
            .sink { [weak self] _ in
                self?.handleObjectWillChange()
            }
            .store(in: &sinks)
    }
    
    // 单个属性变更的处理逻辑(替换成你的序列化等操作)
    private func handlePropertyChange(for propertyName: String) {
        print("属性\(propertyName)已变更,执行模型序列化")
        // 这里添加你的自定义业务逻辑
    }
    
    // 对象整体变更的处理逻辑
    private func handleObjectWillChange() {
        print("对象即将发生变更")
        // 可选:添加对象级别的统一处理逻辑
    }
}

// 辅助协议,用来擦除Published的具体值类型
private protocol AnyPublished {
    var publisher: AnyPublisher<Void, Never> { get }
}

extension Published: AnyPublished {
    var publisher: AnyPublisher<Void, Never> {
        projectedValue
            .map { _ in () }
            .eraseToAnyPublisher()
    }
}

关键说明

  1. 识别@Published属性:通过AnyPublished协议擦除Published的具体值类型,统一处理不同类型的@Published属性,再通过反射筛选出符合条件的属性。
  2. SwiftUI的底层逻辑:SwiftUI并没有逐个订阅@Published属性,而是通过监听ObservableObject的objectWillChange publisher实现视图重绘——所有@Published属性的变更都会自动触发objectWillChange发送事件。如果你的场景只需要在对象变更时执行统一操作,直接订阅objectWillChange会更高效;如果需要针对单个属性做差异化处理,再用上述逐个订阅的方式。
  3. 内存管理:用Set<AnyCancellable>存储订阅引用,确保订阅生命周期和Observed实例一致,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 13:57:42