在 Sheet 消失后将数据写入 UserDefaults 时发生崩溃 (SIGABRT)
Crash (SIGABRT) when writing data to UserDefaults after Sheet disappears
我收到了三个无法重现的类似崩溃报告(全部在 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)
发生的事情是,我有一个带按钮的 Sheet,单击它时 sheet 消失,在 onDisappear
中的 changeCounter
方法调用主视图 MyView
来更改 counter
。当calling/opening Sheet.
时,方法changeCounter
从MyView
传递给Sheet
这是MyView
中的.sheet方法:
.sheet(item: $activeSheet) { item in
switch item {
case .MY_SHEET:
MySheet(changeCounter: {changeCounter(decrease: true)}, changeTimer, item: $activeSheet)
}
}
这是(的重要部分)sheet:
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!()
}
})
}
}
这里是 changeCounter
和 preferences
对象:
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
。
任何人都知道崩溃是如何发生的以及在什么情况下发生的?因为它现在在用户设备上只发生了三次,我无法重现。
我们来分析一下
Button(action: {
self.dismissAction = changeCounter 1)
self.item = nil 2)
}, label: {
第 1 行更改内部 sheet 状态,启动 sheet 视图的更新
第 2 行)更改外部状态以启动 sheet 的关闭(并且可能更新父视图)。
它甚至听起来像是两个相互冲突的过程(即使没有依赖流,但看你的代码第二取决于第一个的结果)。所以,这是非常危险的逻辑,应该避免。
一般来说,正如我在评论中所写,在一个闭包中更改两个状态总是有风险的,因此我会重写逻辑以使其具有类似(草图)的内容:
Button(action: {
self.result = changeCounter // one external binding !!
}, label: {
,即。启动一些外部 activity...
的一个状态变化
您的代码的可能解决方法(如果出于任何原因您无法更改逻辑)是及时分离这些状态的更改,例如
Button(action: {
self.dismissAction = changeCounter // updates sheet
DispatchQueue.main.async { // or after some min delay
self.item = nil // closes sheet after (!) update
}
}, label: {
我收到了三个无法重现的类似崩溃报告(全部在 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)
发生的事情是,我有一个带按钮的 Sheet,单击它时 sheet 消失,在 onDisappear
中的 changeCounter
方法调用主视图 MyView
来更改 counter
。当calling/opening Sheet.
changeCounter
从MyView
传递给Sheet
这是MyView
中的.sheet方法:
.sheet(item: $activeSheet) { item in
switch item {
case .MY_SHEET:
MySheet(changeCounter: {changeCounter(decrease: true)}, changeTimer, item: $activeSheet)
}
}
这是(的重要部分)sheet:
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!()
}
})
}
}
这里是 changeCounter
和 preferences
对象:
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
。
任何人都知道崩溃是如何发生的以及在什么情况下发生的?因为它现在在用户设备上只发生了三次,我无法重现。
我们来分析一下
Button(action: {
self.dismissAction = changeCounter 1)
self.item = nil 2)
}, label: {
第 1 行更改内部 sheet 状态,启动 sheet 视图的更新 第 2 行)更改外部状态以启动 sheet 的关闭(并且可能更新父视图)。
它甚至听起来像是两个相互冲突的过程(即使没有依赖流,但看你的代码第二取决于第一个的结果)。所以,这是非常危险的逻辑,应该避免。
一般来说,正如我在评论中所写,在一个闭包中更改两个状态总是有风险的,因此我会重写逻辑以使其具有类似(草图)的内容:
Button(action: {
self.result = changeCounter // one external binding !!
}, label: {
,即。启动一些外部 activity...
的一个状态变化您的代码的可能解决方法(如果出于任何原因您无法更改逻辑)是及时分离这些状态的更改,例如
Button(action: {
self.dismissAction = changeCounter // updates sheet
DispatchQueue.main.async { // or after some min delay
self.item = nil // closes sheet after (!) update
}
}, label: {