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

SwiftUI中.refreshable闭包无法正确访问model?这是Bug吗?

问题分析与解决方案

这并非refreshable的预期行为,问题根源在于值类型Model的传递机制与SwiftUI视图更新逻辑的交互冲突。

问题本质

  1. Model是值类型(struct),调用mutating方法修改selectedValue时,本质是生成新的Model实例并替换原属性。虽然@Observable类会检测到这个赋值操作并触发ContentView重新渲染,让NewView拿到新的Model实例,但refreshable的异步闭包可能捕获了NewView初始化时的旧Model副本——当ContentView重新渲染生成新的NewView实例时,旧实例的refreshable闭包可能仍被系统保留,导致下拉刷新时访问的是旧值。
  2. 按钮的点击闭包是同步执行的,每次NewView重新创建时都会绑定新的Model实例,因此能拿到最新值。

修复方案

方案1:将Model改为引用类型(class)

把Model从struct改为class,让所有视图共享同一个实例,无论何时访问都是最新值:

@Observable
class Model {
    var selectedValue = 0
    
    func setValue(_ value: Int) {
        self.selectedValue = value
    }
}

@Observable
class ContentViewController {
    var model = Model()
    
    func setValue(_ value: Int) {
        self.model.setValue(value)
    }
}

方案2:简化状态管理,直接在ContentViewController中维护状态

去掉Model结构体,将selectedValue直接作为ContentViewController的属性,避免值类型传递带来的副本问题:

@Observable
class ContentViewController {
    var selectedValue = 0
    
    func setValue(_ value: Int) {
        self.selectedValue = value
    }
}

// 修改ContentView中的NewView调用
struct ContentView: View {
    @State private var cntrl = ContentViewController()
    @State private var counter = 0
    
    var body: some View {
        VStack {
            Button {
                counter += 1
                cntrl.setValue(counter)
            } label: {
                Text("Increase value")
            }
            
            Text("Selected value local: \(counter)")
            Text("Selected value cntrl: \(cntrl.selectedValue)")
            NewView(selectedValue: cntrl.selectedValue)
        }
    }
}

// 修改NewView
struct NewView:View {
    let selectedValue: Int
    
    @State private var valueSetByFunc = 0
    var body: some View {
        VStack {
            Button {
                whatIsTheValue()
            } label: {
                Text("Call func")
            }
            List {
                Text("Value set by func: \(valueSetByFunc)")
            }.refreshable {
                whatIsTheValue()
            }
        }
    }
    
    private func whatIsTheValue() {
        valueSetByFunc = selectedValue
    }
}

方案3:让Model成为可观察值类型(iOS 17+)

给Model添加@Observable宏,确保其内部属性变化能被SwiftUI正确感知:

@Observable
struct Model {
    var selectedValue = 0
    
    mutating func setValue(_ value: Int) {
        self.selectedValue = value
    }
}

此时ContentViewController的model属性变化会准确触发视图更新,NewView每次都会拿到最新的Model实例,refreshable闭包也会捕获最新值。

总结

值类型的状态传递在SwiftUI中需要注意视图更新时机,异步闭包(如refreshable)容易捕获旧的副本。改用引用类型或简化顶层状态管理,能更可靠地保证状态一致性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 04:24:54