SwiftUI @AppStroage - 'Simultaneous accesses to *, but modification requires exclusive access'
SwiftUI @AppStroage - 'Simultaneous accesses to *, but modification requires exclusive access'
在我的项目中,我正在处理这个问题。几个小时后,我能够找到原因并将它们简化为一个小演示,但不了解真正的问题。正如您在下面的代码中看到的,这里有很多东西在起作用,如果使用下面提到的任何“安全”替代方案,错误就不会浮出水面。这是 Swift 内存访问问题,还是 SwiftUI 错误?
重现步骤:
- 将以下代码复制到项目中
- 基于 iPad mini
- 点击后退按钮
崩溃
import SwiftUI
/*
Reproduction Steps:
===================
#1. Build on iPad mini (other devices will work in split screen, but Detail view must be showing, sidebar view must be hidden)
#2. Tap Back Button
=====================
Crash
Thread 1: Simultaneous accesses to 0x7f8af986bae0, but modification requires exclusive access
// Note: if any of the "safe" alternatives are used below, the bug won't surface.
*/
// MARK: - Model
public struct SafeSetting<V> {
public let key: String
public let defaultValue: V
public init(name: String, defaultValue: V) {
self.key = name
self.defaultValue = defaultValue
}
}
public struct CrashSetting<V> {
public let key: String
public let defaultValue: V
public init(namespace: String, name: String, defaultValue: V) {
/// - NOTE: I believe this is the main issue, but why?
self.key = String(namespace + "." + name)
self.defaultValue = defaultValue
}
}
enum Settings {
static let safe = SafeSetting(name: "safe", defaultValue: "")
static let crash = CrashSetting(namespace: "com.mynamespace", name: "crash", defaultValue: "")
}
// MARK: - View
@main
struct NavigationLinkCrashApp: App {
var body: some Scene {
WindowGroup {
RootView()
}
}
}
struct RootView: View {
// MARK: Safe
/*
@AppStorage(Settings.safe.key)
var safe = Settings.safe.defaultValue
*/
// MARK: Crash
@AppStorage(Settings.crash.key)
var crash = Settings.crash.defaultValue
@ViewBuilder var content: some View {
NavigationView {
Sidebar()
Color.yellow
}
}
var body: some View {
content
}
}
struct Sidebar: View {
var body: some View {
// List { NavigationLink } hierarchy is needed!
List {
NavigationLink {
Color.red
} label: {
// MARK: Safe
/// `Text("")`
/// `Label("", systemImage: "circle")`
/// `Color.green`
// MARK: Crash
Text("crashes")
//Label("crashes", systemImage: "circle")
}
}
// List Style is needed!
// MARK: Safe
//.listStyle(SidebarListStyle())
//.listStyle(InsetListStyle())
// MARK: Crash
.listStyle(InsetGroupedListStyle())
//.listStyle(PlainListStyle())
//.listStyle(GroupedListStyle())
}
}
我将把这归因于 SwiftUI 错误。我已将其提交给 Apple (FB9975464)。问题似乎出在 CrashSetting
初始值设定项中,但实际上没有它就会发生。我认为问题出在 AppStorage 密钥中使用点 (.
) 字符。也造成了[error] precondition failure: setting value during update: 5848
。我没能用那个崩溃制作一个小演示,但删除了“。”从我的钥匙也解决了它。如果您遇到上述任何情况,特别是在 transitioning/navigating 次查看时,值得检查您的 AppStorage 密钥。
最简单的崩溃演示
- 基于 iPad mini
- 点击后退按钮
import SwiftUI
@main
struct AppStorage_Key_IssueApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
// Using the "." character results in many hard to track crashes
@AppStorage("a.b")
var val = ""
var body: some View {
NavigationView {
PrimaryView()
Color.blue
}
}
}
struct PrimaryView: View {
var body: some View {
List {
NavigationLink {
Color.red
} label: {
Text("crashes")
}
}
.listStyle(InsetGroupedListStyle())
}
}
在我的项目中,我正在处理这个问题。几个小时后,我能够找到原因并将它们简化为一个小演示,但不了解真正的问题。正如您在下面的代码中看到的,这里有很多东西在起作用,如果使用下面提到的任何“安全”替代方案,错误就不会浮出水面。这是 Swift 内存访问问题,还是 SwiftUI 错误?
重现步骤:
- 将以下代码复制到项目中
- 基于 iPad mini
- 点击后退按钮
崩溃
import SwiftUI
/*
Reproduction Steps:
===================
#1. Build on iPad mini (other devices will work in split screen, but Detail view must be showing, sidebar view must be hidden)
#2. Tap Back Button
=====================
Crash
Thread 1: Simultaneous accesses to 0x7f8af986bae0, but modification requires exclusive access
// Note: if any of the "safe" alternatives are used below, the bug won't surface.
*/
// MARK: - Model
public struct SafeSetting<V> {
public let key: String
public let defaultValue: V
public init(name: String, defaultValue: V) {
self.key = name
self.defaultValue = defaultValue
}
}
public struct CrashSetting<V> {
public let key: String
public let defaultValue: V
public init(namespace: String, name: String, defaultValue: V) {
/// - NOTE: I believe this is the main issue, but why?
self.key = String(namespace + "." + name)
self.defaultValue = defaultValue
}
}
enum Settings {
static let safe = SafeSetting(name: "safe", defaultValue: "")
static let crash = CrashSetting(namespace: "com.mynamespace", name: "crash", defaultValue: "")
}
// MARK: - View
@main
struct NavigationLinkCrashApp: App {
var body: some Scene {
WindowGroup {
RootView()
}
}
}
struct RootView: View {
// MARK: Safe
/*
@AppStorage(Settings.safe.key)
var safe = Settings.safe.defaultValue
*/
// MARK: Crash
@AppStorage(Settings.crash.key)
var crash = Settings.crash.defaultValue
@ViewBuilder var content: some View {
NavigationView {
Sidebar()
Color.yellow
}
}
var body: some View {
content
}
}
struct Sidebar: View {
var body: some View {
// List { NavigationLink } hierarchy is needed!
List {
NavigationLink {
Color.red
} label: {
// MARK: Safe
/// `Text("")`
/// `Label("", systemImage: "circle")`
/// `Color.green`
// MARK: Crash
Text("crashes")
//Label("crashes", systemImage: "circle")
}
}
// List Style is needed!
// MARK: Safe
//.listStyle(SidebarListStyle())
//.listStyle(InsetListStyle())
// MARK: Crash
.listStyle(InsetGroupedListStyle())
//.listStyle(PlainListStyle())
//.listStyle(GroupedListStyle())
}
}
我将把这归因于 SwiftUI 错误。我已将其提交给 Apple (FB9975464)。问题似乎出在 CrashSetting
初始值设定项中,但实际上没有它就会发生。我认为问题出在 AppStorage 密钥中使用点 (.
) 字符。也造成了[error] precondition failure: setting value during update: 5848
。我没能用那个崩溃制作一个小演示,但删除了“。”从我的钥匙也解决了它。如果您遇到上述任何情况,特别是在 transitioning/navigating 次查看时,值得检查您的 AppStorage 密钥。
最简单的崩溃演示
- 基于 iPad mini
- 点击后退按钮
import SwiftUI
@main
struct AppStorage_Key_IssueApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
// Using the "." character results in many hard to track crashes
@AppStorage("a.b")
var val = ""
var body: some View {
NavigationView {
PrimaryView()
Color.blue
}
}
}
struct PrimaryView: View {
var body: some View {
List {
NavigationLink {
Color.red
} label: {
Text("crashes")
}
}
.listStyle(InsetGroupedListStyle())
}
}