SwiftUI macOS应用移除携带EnvironmentObject的视图时崩溃如何解决
问题原因
核心问题是ForEach(data.items.indices, id:\.hashValue)的遍历方式本身存在数组越界风险,EnvironmentObject只是触发崩溃的诱因:
- 遍历数组索引生成视图时,SwiftUI会缓存索引与对应视图的映射关系,删除数组元素后数组长度变小,但ForEach的视图更新存在时序差:会先尝试访问原索引对应的数组元素,此时就会触发越界崩溃。
- 去掉
@EnvironmentObject未崩溃只是偶然现象:没有EnvironmentObject的情况下,删除数组元素的变更没有触发非法索引对应的视图刷新,所以暂时没有踩到越界的坑;加了EnvironmentObject后,ObservableObject的变更会强制所有关联视图刷新,刚好触发了越界访问,所以表现为加了EnvironmentObject才崩溃。
解决方案
推荐两种修复方式,优先使用第一种:
方案1:直接遍历数组元素(推荐)
你的Item已经实现了Identifiable协议,可以直接遍历绑定数组,完全规避下标访问的越界风险,修改后的ContentView代码如下:
struct ContentView: View { @StateObject var data = Data() var body: some View { VStack { ForEach($data.items) { $item in SubView(item: $item) .environmentObject(data) } Button(action: data.removeOneItem) { Text("Remove one item").foregroundColor(.red) } } } }
该写法直接基于元素本身的唯一ID生成视图,数组增删时不会出现索引不匹配的问题。
方案2:索引访问前加合法校验
如果业务逻辑必须用到元素在数组中的索引,可在下标访问前增加合法性判断,避免越界:
ForEach(data.items.indices, id:\.self) { i in if i < data.items.count { SubView(item: $data.items[i]) .environmentObject(data) } }
注意这里将id从\.hashValue改为\.self,索引的hashValue不稳定,直接用索引本身作为id更合理。
内容的提问来源于stack exchange,提问作者Jayåk
相关产品推荐
相关产品推荐

