Swift/SwiftUI中结构体修改的属性值在DispatchQueue相关函数内未更新怎么解决
问题成因
- 核心原因是Swift中的结构体属于值类型,所有传递、闭包捕获场景都会自动生成独立的副本,不同副本的属性修改完全互不影响。
- 调用
start()方法时,当前CustomType实例的value被设为true,调用dispatchQueueFuction时,方法内部拿到的是这个value=true的实例副本。 DispatchQueue.main.asyncAfter的逃逸闭包会捕获当前上下文的self副本,后续递归调用dispatchQueueFuction时使用的永远是最初捕获的旧副本,完全感知不到外部调用stop()修改的是@State管理的新的CustomType实例。- 新增的计算属性
getValue没有作用,因为它读取的是当前所属副本的value,旧副本的value从未被修改,永远返回true。
解决方案
方案1:将结构体改为类(推荐,改动最小)
将CustomType声明为引用类型的类,所有上下文共享同一个实例的引用,属性修改会同步到所有引用位置,修改后代码如下:
import SwiftUI import Combine class CustomType: ObservableObject { var value: Bool = false var getValue: Bool { value } func start() { value = true print("self.value =", value) dispatchQueueFuction() } func dispatchQueueFuction() { guard getValue else { return } DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in guard let self = self else { return } print("Hello, World!") self.dispatchQueueFuction() } } func stop() { value = false print("self.value =", value) } }
对应的ContentView需要同步修改属性声明,适配引用类型的状态管理:
struct ContentView: View { @StateObject private var customType: CustomType = CustomType() var body: some View { VStack { Button("start") { customType.start() }.padding() Button("stop") { customType.stop() }.padding() } } }
方案2:保留结构体,调度逻辑外移
如果必须使用结构体,可以把递归调度的逻辑移到ContentView层,直接操作@State管理的最新结构体实例,避免闭包捕获旧副本的问题。
内容的提问来源于stack exchange,提问作者swiftPunk
相关产品推荐
相关产品推荐

