惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
SegmentFault 最新的问题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
B
Blog RSS Feed
Y
Y Combinator Blog
T
Tailwind CSS Blog
博客园 - 三生石上(FineUI控件)
J
Java Code Geeks
Stack Overflow Blog
Stack Overflow Blog
aimingoo的专栏
aimingoo的专栏
Jina AI
Jina AI
The GitHub Blog
The GitHub Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
A
About on SuperTechFans
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
Docker
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Check Point Blog
M
MIT News - Artificial intelligence
Last Week in AI
Last Week in AI
V
V2EX
腾讯CDC
F
Fortinet All Blogs
博客园 - 叶小钗
T
The Blog of Author Tim Ferriss

肘子的 Swift 记事本 | Fatbobman's Blog

iPhone Duo 带来的机遇与挑战 -- 肘子的 Swift 周报 #153 当 Mac mini 的价格不再 mini -- 肘子的 Swift 周报 #152 SwiftData:优化从建模开始 举手之劳 -- 肘子的 Swift 周报 #151 从使用 AI 到委托 AI(下):我想要的可委托性 AI 想放弃了,人没有 -- 肘子的 Swift 周报 #150 从使用 AI 到委托 AI:我的一些思考 越来越慢的 App Review,拦住了谁? -- 肘子的 Swift 周报 #149 ContentBuilder 解析:SwiftUI 类型检查性能提升的秘密 Apple Intelligence 已通过中国政府审核,即将在中国提供服务 -- 肘子的 Swift 周报 #148 在 SwiftUI Text 中控制孤行:挖掘未公开的 avoidsOrphans 热茶还是冰咖啡 -- 肘子的 Swift 周报 #147 Liquid Glass:UIKit 适配踩坑实录 不是模型变慢了,是任务变大了 -- 肘子的 Swift 周报 #146 当灵感跑在了结果前面 -- 肘子的 Swift 周报 #145 当每一次写入都有了新价格 -- 肘子的 Swift 周报 #144 当 Linux 成为“空气”:容器、Agent 与不再重要的“桌面之争” -- 肘子的 Swift 周报 #143 两个 SwiftUI 动画 Bug 排查小记 SPI 加入 Apple,Swift 迈向自举 -- 肘子的 Swift 周报 #142 Swift 还让你 Excited 吗? -- 肘子的 Swift 周报 #141 从 Size Class 到可用空间,horizontalSizeClass 还可靠吗? WWDC 26:AI 帮你看完了,然后呢? -- 肘子的 Swift 周报 #140 WWDC 2026 初印象:符合预期,但更务实 -- 肘子的 Swift 周报 #139 Core Data + Observation:从属性级响应到心智解放 稳定 > 新功能 -- 肘子的 Swift 周报 #138 用自定义 Layout 化解 SwiftUI List 的行高与间距跳变 从社区路标到生态基石:Dave Verwer 的新篇章 -- 肘子的 Swift 周报 #137 消失的 WWDC 愿望单 -- 肘子的 Swift 周报 #136 CocoaPods 正在退场,SwiftPM 才刚到第二章 -- 肘子的 Swift 周报 #135 让 AI 从称手到称心 -- 肘子的 Swift 周报 #134
为什么父视图无法修改子视图的 @State
东坡肘子 · 2025-12-18 · via 肘子的 Swift 记事本 | Fatbobman's Blog

核心摘要@State 旨在管理视图的内部私有状态,其初始值仅在视图 第一次建立标识(Identity) 时生效;若需持续响应外部数据的变化,请使用 @Binding 或普通的 let 属性。

问题现象

很多 SwiftUI 初学者经常会遇到这样一个“奇怪”的现象:

一个子视图包含两个参数,一个使用 @State 修饰,另一个没有。在父视图中更新这两个参数对应的数据源时,只有没有标注 @State 的属性会随之更新,而标注了 @State 的属性则“无动于衷”。

struct SubView: View {
    let normalValue: String
    @State var stateValue: String // 父视图更新时,这里不会变
    
    // ...
}

@State 的核心语义

SwiftUI 作为一个声明式响应框架,@State 是其最基础的状态管理工具。它的核心语义只有一条:

由 SwiftUI 运行时负责存储和管理内存,确保由 @State 持有的值,其生命周期与视图的生命周期(Identity)完全一致。

这意味着 @State 显式地定义了一个“私域”属性。一旦视图被分配了存储空间,该属性就由视图内部全权接管。

@State var name = "abc"
// 语义上等同于
@State private var name = "abc"

因此,苹果官方文档和最佳实践都强烈建议将 @State 声明为 private,以强化这一概念:这是视图的私有财产,不应由外部干涉。

为什么父视图无法修改它?

为了贯彻 @State 的语义,SwiftUI 在底层实现中采用了类似“一次性初始化”的策略。

  1. 首次创建:当视图第一次被加载到视图树中(建立 Identity)时,SwiftUI 会根据构造函数(init)中传入的参数,初始化 @State 的内部存储(State<T>)。
  2. 后续更新:当父视图刷新并重新调用子视图的 init 方法时,虽然传入了新的参数值,但 SwiftUI 检测到该视图的存储已经存在。为了保持状态的连续性(即防止用户输入的内容丢失),SwiftUI 会直接忽略 init 中传入的新值,继续使用内部存储中现有的值。

简单来说,@State 就像一个只在搬新家时才接受礼物的盒子,之后的每一次拜访,它都会拒绝接收新的礼物,以保护盒子里已有的东西。

扩展阅读:如果你对底层实现感兴趣,可以深入了解 DynamicProperty 协议@State 在内部维护了一个状态枚举,只有在特定的初始化阶段才会接受外部赋值。

解决方案与实践总结

  1. 区分数据源

    • 如果数据由父视图拥有并控制,子视图只负责展示,请使用普通的 let 属性。
    • 如果子视图需要修改这份数据并同步回父视图,请使用 @Binding
    • 只有当数据是子视图完全私有的内部状态(如按钮的高亮状态、输入框的临时内容)时,才使用 @State
  2. 强制刷新(不推荐)

    • 如果确实需要重置 @State,可以通过改变视图的 id.id(value))来强制 SwiftUI 销毁并重建视图,从而触发新的初始化流程。但这会带来性能开销和状态丢失。
  3. 引用类型的新选择

    • 从 iOS 17 起,配合 Observation 框架,@State 取代 @StateObject 用于持有 Observable 对象。
    • ⚠️ 注意性能:与 @StateObject 不同,@State 不具备懒加载(Lazy Initialization)机制。请避免在 View 的 init 或属性默认值中直接初始化开销巨大的引用类型对象,否则每次视图重绘都会导致对象被重复创建(尽管之后会被立即丢弃)。在确实需要使用懒加载的场景,可以通过自定义 @LazyState 来应对
// ❌ 避免这样做,HeavyObject 会被重复创建
@State private var store = HeavyObject() 
   
// ✅ 推荐:在 onAppear 中或使用 lazy 模式初始化

相关提示

订阅 Fatbobman 周报

每周精选 Swift 与 SwiftUI 开发技巧,加入众多开发者的行列。

立即订阅