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

SwiftUI中@ObservedObject赋值报错:Cannot assign to property: 'self' is immutable

SwiftUI中refreshable闭包无法赋值ObservedObject的解决方法

错误原因

  1. SwiftUI的refreshable闭包默认捕获不可变的self,无法直接修改视图结构体的属性
  2. @ObservedObject用于接收父视图传递的ObservableObject实例,视图自身创建的ObservableObject应该使用@StateObject
  3. 直接重新创建DataSource实例不是最优做法,应该通过调用实例方法刷新数据

解决方法

方案一:修正属性包装器并允许闭包修改实例

将@ObservedObject改为@StateObject,@StateObject支持在闭包中直接赋值更新实例:

struct ContentView: View {
    @State var editMode: EditMode = .inactive
    // 改用@StateObject管理视图自身创建的ObservableObject
    @StateObject var accept = DataSource() 
    var body: some View {
        VStack {
            List {
                ForEach(accept.accepted) { acceptedItem in
                    Text(acceptedItem.number)
                        .bold()
                        .font(.title2)
                    Text("\(acceptedItem.Carrier)")
                        .font(.title3)
                    let que = acceptedItem.events[0]
                    Text("\(que.description)")
                        .font(.title3)
                }
            }
            .refreshable {
                // 重新创建DataSource实例并赋值
                accept = DataSource()
            }
        }
    }
}

方案二:给DataSource添加刷新方法(推荐)

复用现有实例刷新数据,避免重复创建实例带来的资源浪费:

  1. 修改DataSource类,添加刷新方法并处理旧数据和重复请求:
class DataSource: ObservableObject {
    @Published var accepted = [Accepted]()
    private var cancellable: AnyCancellable?
    
    init() {
        gettrackinfoTrack()
    }
    
    // 添加刷新方法:先清空旧数据,再重新请求
    func refresh() {
        accepted.removeAll()
        gettrackinfoTrack()
    }
    
    func gettrackinfoTrack() {
        cancellable?.cancel() // 取消之前的请求,避免重复请求导致的数据混乱
        cancellable = giphyProvider.requestPublisher(.gettrackinfo)
            .mapString()
            .sink(receiveCompletion: { completion in
                guard case let .failure(error) = completion else { return }
                print("查询错误:\(String(describing: error.errorDescription))")
            }, receiveValue: { response in
                guard let data = response.data(using: .utf8) else { return }
                if let json = try? JSON(data: data) {
                    let context = json["data"]["accepted"]
                    for (_, subJson) in context {
                        if let acceptedItem = Accepted.accepted_Build(json: subJson, events: Question.build(json: subJson)!) {
                            self.accepted.append(acceptedItem)
                        }
                    }
                }
            })
    }
}
  1. 修改ContentView的refreshable闭包,直接调用实例的刷新方法:
struct ContentView: View {
    @State var editMode: EditMode = .inactive
    @StateObject var accept = DataSource()
    var body: some View {
        VStack {
            List {
                ForEach(accept.accepted) { acceptedItem in
                    Text(acceptedItem.number)
                        .bold()
                        .font(.title2)
                    Text("\(acceptedItem.Carrier)")
                        .font(.title3)
                    let que = acceptedItem.events[0]
                    Text("\(que.description)")
                        .font(.title3)
                }
            }
            .refreshable {
                // 调用实例的刷新方法,无需重新创建实例
                accept.refresh()
            }
        }
    }
}

关键说明

  • @StateObject是SwiftUI 2.0+引入的,专门用于管理视图自身创建的ObservableObject实例,其生命周期与视图绑定
  • 方案二更符合ObservableObject的设计逻辑,能避免重复创建实例带来的资源消耗和状态丢失问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 12:25:13