无@Published属性的类用@StateObject避免重初始化是否合理?
核心结论
要避免视图刷新时重新初始化Downloader实例,必须使用@StateObject;让无@Published属性的类遵循ObservableObject并非不良开发实践,反而契合SwiftUI的生命周期管理逻辑。
具体分析
1. 为什么必须用@StateObject?
SwiftUI视图是值类型,每次视图状态变化(比如父视图刷新、自身绑定状态更新)时,视图结构体都会被重新创建。如果像示例里那样直接在视图内初始化Downloader(哪怕修正语法写成let downloader = Downloader()),每次刷新都会生成新实例,导致之前的状态(比如isDownloading)丢失,下载任务也会被中断或重复执行。
@StateObject的核心作用就是让SwiftUI接管对象的生命周期:它会把对象实例存储在SwiftUI的内部环境中,不管视图结构体怎么重建,这个实例都会被保留,只有当视图被销毁时才会释放,完美解决“避免重初始化”的需求。
2. 无@Published属性遵循ObservableObject是否合理?
完全合理。ObservableObject协议本质是定义“可被观察”的对象,但它不强制要求必须有@Published属性。如果你不需要向视图发布状态变化(比如不需要UI根据isDownloading更新),只是需要SwiftUI帮你管理对象的存活时间,遵循ObservableObject是合规的开发方式,不存在“不良实践”的问题。
修正后的示例代码
class Downloader: ObservableObject { private var isDownloading = false func download(for id: String) { isDownloading = true // 模拟异步下载任务 DispatchQueue.global().asyncAfter(deadline: .now() + 2) { [weak self] in self?.isDownloading = false print("ID: \(id) 下载完成") } } } struct DummyView: View { @StateObject private var downloader = Downloader() var body: some View { Text("Hello") .onAppear { downloader.download(for: "id") } } }
额外说明
如果你的Downloader需要在多个视图间共享,可以考虑依赖注入(比如EnvironmentObject)或单例模式,但这是另一类场景。如果只是单个视图需要持有独立的Downloader实例并维持其生命周期,@StateObject是最直接且正确的选择。
内容的提问来源于stack exchange,提问作者Boyan Pavlov

