You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sheet消失后写入UserDefaults时触发SIGABRT崩溃问题排查

分析与解决方案

从你提供的崩溃堆栈和代码来看,这个崩溃的核心原因是SwiftUI在iOS 14.4版本中,视图生命周期与ObservableObject更新的冲突导致的已释放unowned引用访问。具体拆解如下:

崩溃根源定位

堆栈中的swift_abortRetainUnowned和swift_unknownObjectUnownedLoadStrong明确指向:代码尝试访问一个已经被销毁的unowned对象。结合调用链来看,问题发生在:

  1. Sheet的onDisappear回调中执行changeCounter,修改了Preferences的@Published属性counter
  2. Combine发送objectWillChange通知,触发SwiftUI的AttributeGraph更新视图
  3. 此时Sheet已经被从视图树中移除,SwiftUI正在清理相关资源,导致视图Graph中的某个unowned delegate引用已经失效,最终触发崩溃

iOS 14.4作为SwiftUI的早期版本,存在不少视图生命周期与状态更新的兼容性bug,尤其是Sheet关闭时的onDisappear执行时机与视图资源清理的顺序问题——这也是你无法复现但用户设备上出现崩溃的核心原因。

可能的触发场景

  • 用户快速操作:短时间内重复打开/关闭Sheet,导致视图清理与状态更新的并发冲突
  • iOS 14.4系统特定的生命周期bug:Sheet的onDisappear在视图实例已被部分销毁后才执行,此时触发状态更新会访问失效的引用

修复方案

针对这个问题,最直接有效的修复是避免在Sheet的onDisappear中触发ObservableObject的状态更新,调整执行时机到Sheet关闭之前。具体修改如下:

1. 移除dismissAction和onDisappear,直接在按钮点击时执行回调

修改Sheet的按钮逻辑,先执行状态更新操作,再关闭Sheet:

struct MySheet: View {
    var changeCounter: () -> Void
    var changeTimer: () -> Void
    @Binding var item: ActiveSheet?
    
    var body: some View {
        GeometryReader { metrics in
            VStack {
                Button(action: {
                    changeCounter() // 先执行计数器修改
                    item = nil // 再关闭Sheet
                }, label: {
                    Text("change_counter")
                })
                Button(action: {
                    changeTimer() // 先执行计时器修改
                    item = nil // 再关闭Sheet
                }, label: {
                    Text("change_timer")
                })
            }.frame(width: metrics.size.width, height: metrics.size.height * 0.85)
        }
    }
}

2. 增加闭包的弱引用捕获(可选,提升安全性)

为了避免极端情况下的引用失效,在传递回调给Sheet时,使用weak self捕获主视图实例:

.sheet(item: $activeSheet) { item in
    switch item {
    case .MY_SHEET:
        MySheet(
            changeCounter: { [weak self] in self?.changeCounter(decrease: true) },
            changeTimer: changeTimer,
            item: $activeSheet
        )
    }
}

3. 针对iOS 14的额外优化(可选)

如果你需要兼容iOS 14,可以考虑在状态更新时增加轻微延迟,避免与视图清理操作冲突:

func changeCounter(decrease: Bool) {
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        if decrease {
            preferences.counter -= COUNTER_INTERVAL
        }
    }
}

验证建议

由于你无法复现该问题,建议在iOS 14.4的模拟器或测试设备上,尝试快速重复打开/关闭Sheet,模拟用户的快速操作,验证修复后的代码是否能避免崩溃。

内容的提问来源于stack exchange,提问作者L3n95

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 15:07:45