在工作表消失后将数据写入 UserDefaults 时崩溃 (SIGABRT)

Posted

技术标签:

【中文标题】在工作表消失后将数据写入 UserDefaults 时崩溃 (SIGABRT)【英文标题】:Crash (SIGABRT) when writing data to UserDefaults after Sheet disappears 【发布时间】:2021-04-29 15:09:47 【问题描述】:

我收到了三个无法重现的类似崩溃报告(全部在 ios 14.4 上)。 stracktrace 说明如下(我只粘贴了我的应用程序启动的部分):

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Triggered by Thread:  0

Thread 0 name:
Thread 0 Crashed:
0   libsystem_kernel.dylib          0x00000001c077d414 __pthread_kill + 8
1   libsystem_pthread.dylib         0x00000001de2d8b50 pthread_kill + 272 (pthread.c:1392)
2   libsystem_c.dylib               0x000000019bc5bb74 abort + 104 (abort.c:110)
3   libswiftCore.dylib              0x0000000196795f20 swift::fatalError(unsigned int, char const*, ...) + 60 (Errors.cpp:393)
4   libswiftCore.dylib              0x0000000196796078 swift::swift_abortRetainUnowned(void const*) + 36 (Errors.cpp:460)
5   libswiftCore.dylib              0x00000001967e5844 swift_unknownObjectUnownedLoadStrong + 76 (SwiftObject.mm:895)
6   SwiftUI                         0x00000001992b0cdc ViewGraph.graphDelegate.getter + 16 (ViewGraph.swift:234)
7   SwiftUI                         0x00000001997e4d58 closure #1 in GraphHost.init(data:) + 80
8   SwiftUI                         0x00000001997e6550 partial apply for closure #1 in GraphHost.init(data:) + 40 (<compiler-generated>:0)
9   AttributeGraph                  0x00000001bbcc9b88 AG::Graph::Context::call_update() + 76 (ag-closure.h:108)
10  AttributeGraph                  0x00000001bbcca1a0 AG::Graph::call_update() + 56 (ag-graph.cc:176)
11  AttributeGraph                  0x00000001bbccfd70 AG::Subgraph::update(unsigned int) + 92 (ag-graph.h:709)
12  SwiftUI                         0x00000001997e1cdc GraphHost.runTransaction() + 172 (GraphHost.swift:491)
13  SwiftUI                         0x00000001997e4e1c GraphHost.runTransaction(_:) + 92 (GraphHost.swift:471)
14  SwiftUI                         0x00000001997e37a8 GraphHost.flushTransactions() + 176 (GraphHost.swift:459)
15  SwiftUI                         0x00000001997e2c78 specialized GraphHost.asyncTransaction<A>(_:mutation:style:) + 252 (<compiler-generated>:0)
16  SwiftUI                         0x00000001993bd2fc AttributeInvalidatingSubscriber.invalidateAttribute() + 236 (AttributeInvalidatingSubscriber.swift:89)
17  SwiftUI                         0x00000001993bd1f8 AttributeInvalidatingSubscriber.receive(_:) + 100 (AttributeInvalidatingSubscriber.swift:53)
18  SwiftUI                         0x00000001993bd914 protocol witness for Subscriber.receive(_:) in conformance AttributeInvalidatingSubscriber<A> + 24 (<compiler-generated>:0)
19  SwiftUI                         0x000000019956ba34 SubscriptionLifetime.Connection.receive(_:) + 100 (SubscriptionLifetime.swift:195)
20  Combine                         0x00000001a6e67900 ObservableObjectPublisher.Inner.send() + 136 (ObservableObject.swift:115)
21  Combine                         0x00000001a6e670a8 ObservableObjectPublisher.send() + 632 (ObservableObject.swift:153)
22  Combine                         0x00000001a6e4ffdc PublishedSubject.send(_:) + 136 (PublishedSubject.swift:82)
23  Combine                         0x00000001a6e76994 specialized static Published.subscript.setter + 388 (Published.swift:0)
24  Combine                         0x00000001a6e75f74 static Published.subscript.setter + 40 (<compiler-generated>:0)
25  MyApp                           0x00000001005d1228 counter.set + 32 (Preferences.swift:0)
26  MyApp                           0x00000001005d1228 Preferences.counter.modify + 120 (Preferences.swift:0)
27  MyApp                           0x00000001005ca440 MyView.changeCounter(decrease:) + 344 (MyView.swift:367)
28  MyApp                           0x00000001005cf110 0x100584000 + 307472
29  MyApp                           0x00000001005e65d8 thunk for @escaping @callee_guaranteed () -> () + 20 (<compiler-generated>:0)
30  MyApp                           0x00000001005a8828 closure #2 in MySheet.body.getter + 140 (MySheet.swift:0)

正在发生的事情是,我有一个带有按钮的工作表,当单击它时,工作表消失,并且在主视图 MyView 中的 onDisappear 中调用 changeCounter 方法来更改 counter .调用/打开工作表时,方法changeCounter 会从MyView 传递给工作表。

这是MyView中的.sheet方法:

.sheet(item: $activeSheet)  item in
    switch item 
    case .MY_SHEET:
        MySheet(changeCounter: changeCounter(decrease: true), changeTimer, item: $activeSheet)
    

这是工作表的(重要部分):

struct MySheet: View 
    var changeCounter: () -> Void
    var changeTimer: () -> Void
    @Binding var item: ActiveSheet?
    @State var dismissAction: (() -> Void)?
    
    var body: some View 
        GeometryReader  metrics in
            VStack 
                Button(action: 
                    self.dismissAction = changeCounter
                    self.item = nil
                , label: 
                    Text("change_counter")
                )
                Button(action: 
                    self.dismissAction = changeTimer
                    self.item = nil
                , label: 
                    Text("change_timer")
                )
            .frame(width: metrics.size.width, height: metrics.size.height * 0.85)
        .onDisappear(perform: 
            if self.dismissAction != nil 
                self.dismissAction!()
            
        )
    

这里是 changeCounterpreferences 对象:

struct MyView: View 
    @EnvironmentObject var preferences: Preferences

    var body: some View ...

    func changeCounter(decrease: Bool) 
        if decrease 
            preferences.counter -= COUNTER_INTERVAL
        
    

Preferences 是带有 counter 变量的 ObservableObject

class Preferences: ObservableObject 
    let userDefaults: UserDefaults

    init(_ userDefaults: UserDefaults) 
        self.userDefaults = userDefaults
        self.counter = 0
    

    @Published var counter: Int 
        didSet 
            self.userDefaults.set(counter, forKey: "counter")
        
    

它会更改userDefaults 中的一个值,即UserDefaults.standard

任何人都知道崩溃是如何发生的以及在什么情况下发生?因为它现在只在用户设备上发生了 3 次,我无法重现。

【问题讨论】:

您是否完全删除了应用程序,可能重置了设备并再次测试?我怀疑这个问题会再次发生。 我的设备上没有发生这种情况。我用模拟器尝试过,但没有崩溃,我也尝试重新安装它。并且用户的描述看起来它也可以多次运行而没有崩溃。 现在发生了两次,都是在 iOS 14.4 上。 您能否展示您的代码,您的工作表是在哪里创建的以及 changeCounter 是在哪里调用的? 在一个动作中改变两个状态可能是问题的根源。众所周知,由于 SwiftUI 2.0 处理不好,所以尝试结合模型(替换、重构等)只改变一种状态。 【参考方案1】:

我们来分析

    Button(action: 
        self.dismissAction = changeCounter       1)
        self.item = nil                          2)
    , label: 

第 1 行)更改内部工作表状态,启动工作表视图的更新 第 2 行) 更改外部状态,开始关闭工作表(并且可能更新父视图)。

这甚至听起来像是两个冲突的过程(即使没有依赖的流程,但是第二个查看您的代码取决于第一个的结果)。所以,这是非常危险的逻辑,应该避免。

一般来说,正如我在评论中所写,在一个闭包中更改两个状态总是有风险的,所以我会重写逻辑以具有类似(草图)的内容:

Button(action: 
    self.result = changeCounter  // one external binding !!
, label: 

,即。启动某些外部活动的一种状态变化...

您的代码的可能解决方法(如果由于任何原因您无法更改逻辑)是及时分离这些状态的更改,例如

Button(action: 
    self.dismissAction = changeCounter // updates sheet
    DispatchQueue.main.async          // or after some min delay
      self.item = nil                  // closes sheet after (!) update
    
, label: 

【讨论】:

在您的第一个 cmets 之后,我首先将操作更改为:self.dismissAction = changeCounter; self.presentationMode.wrappedValue.dismiss()其中presentationMode@Environment(\.presentationMode) var presentationMode。这在我的设备上“有效”。它是否与我的原始代码具有相同的负面影响,还是您认为它是一个有效的解决方案? 几乎一样。

以上是关于在工作表消失后将数据写入 UserDefaults 时崩溃 (SIGABRT)的主要内容,如果未能解决你的问题,请参考以下文章

使用 UserDefaults 保存数据的正确事件是啥?

从 Testflight 安装同一应用程序的不同构建版本时,UserDefaults 正在消失

什么是使用UserDefaults保存数据的适当事件?

在每个迭代步骤后将计算数据写入 txt/csv

在 UserDefaults 中存储 OpaquePointer 类型

节点 - 承诺后将 JSON 结果写入文件