← 返回列表
·
#iOS#SwiftUI#踩坑

SwiftUI 里 Service 千万别用 @State

iOS 新手踩坑:每次重新进页面 Service 都「重置」了,原因是 @State 的生命周期不是你以为的那样

iOS 是我的知识盲区,写 ClawMo 的时候踩了一个不算 bug 的 bug,记一下。

问题

管理页有一个 WebSocketService,负责长连接和会话同步。最初我这么写:

struct AdminPage: View {
  @State private var service = WebSocketService()
  // ...
}

结果每次切到别的 tab 再回来,连接就重连一次、会话列表清空、用户登录态丢失。

原因

@State 是绑定在 当前 View 实例 的存储。SwiftUI 在父视图重建(比如 tab 切换)时,View struct 会被重新创建,@State 的初值会被重新求值——但只在第一次有效,之后由框架托管。

听起来好像没问题,但如果你把 Service 当成「计算属性」或者用 @State 持有一个非 ObservableObject、又不走 EnvironmentObject 注入,行为就会很微妙:在某些重建场景下,框架会判定它需要重置。

正解

Service 必须是单例 + let,View 只持有引用:

final class WebSocketService: ObservableObject {
  static let shared = WebSocketService()
  // ...
}

struct AdminPage: View {
  let service = WebSocketService.shared
  // ...
}

或者顶层用 EnvironmentObject 注入。关键点是:Service 的生命周期不能跟 View 的生命周期绑定。

教训

  • SwiftUI 的属性包装器有一套自己的生命周期模型,不要按 React useState 的直觉去用
  • 长连接、登录态、缓存这种「应用级状态」,永远走单例或 Environment
  • 写 Swift 多让 Claude 解释一下原理,省下来的时间够再写一个页面