使用 NavigationView 和工作表删除元素时 ForEach 崩溃

Posted

技术标签:

【中文标题】使用 NavigationView 和工作表删除元素时 ForEach 崩溃【英文标题】:Crash in ForEach when deleting element with NavigationView and sheet 【发布时间】:2021-08-12 13:07:00 【问题描述】:

使用 Xcode 13 的新 binding variant 或 ForEach 时,当从 @Published 属性支持的内容中删除除最后一个元素之外的任何元素时,我的应用程序在设备上可预见地崩溃(但在模拟器中工作)。

奇怪的是,这只发生在视图包含在 NavigationView 中并附有工作表时。

这是一个演示该问题的独立应用程序:

import SwiftUI

struct Vegetable: Identifiable 
    let id: UUID
    let name: String


class MyModel: ObservableObject 
    @Published var vegetables: [Vegetable] = [
        Vegetable(id: UUID(), name: "Eggplant"),
        Vegetable(id: UUID(), name: "Corn"),
        Vegetable(id: UUID(), name: "Radish")
    ]


@main
struct SwiftUIForEachTestApp: App 
    @StateObject var model = MyModel()
    var body: some Scene 
        WindowGroup 
            ContentView()
                .environmentObject(model)
        
    


struct ContentView: View 
    var body: some View 
        // Remove the containing NavigationView and the crash disappears
        NavigationView 
            MyView()
        
    


struct MyView: View 
    @EnvironmentObject private var model: MyModel
    @State private var isPresentingSheet = false
    var body: some View 
        List 
            ForEach($model.vegetables)  $vegetable in
                Text(vegetable.name)
            
            .onDelete  indices in model.vegetables.remove(atOffsets: indices) 
        
        // Remove the sheet and the crash disappears
        .sheet(isPresented: $isPresentingSheet) 
        
    

运行应用并删除顶行会导致以下崩溃(第 16 行对应于@main):

#0  0x00000001a73a6eac in _swift_runtime_on_report ()
#1  0x00000001a741f100 in _swift_stdlib_reportFatalErrorInFile ()
#2  0x00000001a705e668 in closure #1 in closure #1 in closure #1 in _assertionFailure(_:_:file:line:flags:) ()
#3  0x00000001a705dce0 in _assertionFailure(_:_:file:line:flags:) ()
#4  0x00000001a703c6b4 in _ArrayBuffer._checkInoutAndNativeTypeCheckedBounds(_:wasNativeTypeChecked:) ()
#5  0x00000001a7041e2c in Array.subscript.read ()
#6  0x00000001a7041d58 in protocol witness for Collection.subscript.read in conformance [τ_0_0] ()
#7  0x0000000100320c38 in closure #1 in ForEach<>.init<τ_0_0>(_:id:content:) ()
#8  0x0000000100320f48 in partial apply for closure #1 in ForEach<>.init<τ_0_0>(_:id:content:) ()
#9  0x0000000100320fd8 in thunk for @escaping @callee_guaranteed (@in_guaranteed τ_1_0.Collection.Index) -> (@out τ_1_0.Collection.Index, @out τ_0_1) ()
#10 0x0000000100321090 in partial apply for thunk for @escaping @callee_guaranteed (@in_guaranteed τ_1_0.Collection.Index) -> (@out τ_1_0.Collection.Index, @out τ_0_1) ()
#11 0x00000001a7193d0c in LazyMapSequence<>.subscript.read ()
#12 0x00000001a71938e4 in protocol witness for Collection.subscript.read in conformance <> LazyMapSequence<τ_0_0, τ_0_1> ()
#13 0x00000001aa2a374c in ForEach.IDGenerator.makeID(data:index:offset:) ()
#14 0x00000001aa2abf54 in ForEachState.ForEachViewIDCollection.subscript.getter ()
#15 0x00000001aa2ac914 in ForEachState.ForEachViewIDCollection.subscript.read ()
#16 0x00000001aa2ac84c in protocol witness for Collection.subscript.read in conformance ForEachState<τ_0_0, τ_0_1, τ_0_2>.ForEachViewIDCollection ()
#17 0x00000001aa37a838 in _ViewList_ID._Views.subscript.getter ()
#18 0x00000001aa37a3f4 in protocol witness for Collection.subscript.read in conformance _ViewList_ID.Views ()
#19 0x00000001a71eaecc in Slice.subscript.getter ()
#20 0x00000001a71ed008 in Slice.subscript.read ()
#21 0x00000001a71ecf48 in protocol witness for Collection.subscript.read in conformance Slice<τ_0_0> ()
#22 0x00000001a7193cc8 in LazyMapSequence<>.subscript.read ()
#23 0x00000001a71938e4 in protocol witness for Collection.subscript.read in conformance <> LazyMapSequence<τ_0_0, τ_0_1> ()
#24 0x00000001aa157f44 in ShadowRowCollection.subscript.getter ()
#25 0x00000001aa1580ec in ShadowRowCollection.subscript.read ()
#26 0x00000001aa15802c in protocol witness for Collection.subscript.read in conformance ShadowRowCollection<τ_0_0> ()
#27 0x00000001aa365448 in ListCoreDataSource.rowID(at:) ()
#28 0x00000001aa004ec0 in closure #1 in performUpdates #1 <τ_0_0, τ_0_1>() in ListCoreCoordinator.updateUITableView(_:to:transaction:) ()
#29 0x00000001a9ffd09c in specialized _ArrayProtocol.filter(_:) ()
#30 0x00000001aa004658 in performUpdates #1 <τ_0_0, τ_0_1>() in ListCoreCoordinator.updateUITableView(_:to:transaction:) ()
#31 0x00000001aa00565c in closure #1 in ListCoreCoordinator.updateUITableView(_:to:transaction:) ()
#32 0x00000001a9ec6008 in thunk for @escaping @callee_guaranteed () -> () ()
#33 0x00000001a9eaee84 in static Update.end() ()
#34 0x00000001a9cd7390 in static NSRunLoop.flushObservers() ()
#35 0x00000001a9cd72d8 in closure #1 in closure #1 in static NSRunLoop.addObserver(_:) ()
#36 0x00000001a9cd2250 in specialized thunk for @callee_guaranteed () -> (@error @owned Error) ()
#37 0x00000001c9b53f24 in autoreleasepool<τ_0_0>(invoking:) ()
#38 0x00000001a9cd72b8 in closure #1 in static NSRunLoop.addObserver(_:) ()
#39 0x00000001a9cd7410 in @objc closure #1 in static NSRunLoop.addObserver(_:) ()
#40 0x00000001a3437588 in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#41 0x00000001a34316ac in __CFRunLoopDoObservers ()
#42 0x00000001a3431c58 in __CFRunLoopRun ()
#43 0x00000001a3431308 in CFRunLoopRunSpecific ()
#44 0x00000001baab4734 in GSEventRunModal ()
#45 0x00000001a5eaf75c in -[UIApplication _run] ()
#46 0x00000001a5eb4fcc in UIApplicationMain ()
#47 0x00000001aa39c380 in closure #1 in KitRendererCommon(_:) ()
#48 0x00000001aa39c30c in runApp<τ_0_0>(_:) ()
#49 0x00000001a9e94b38 in static App.main() ()
#50 0x000000010031c9c4 in static SwiftUIForEachTestApp.$main() at /xxx/SwiftUIForEachTest/Shared/ContentView.swift:16
#51 0x000000010031e888 in main ()
#52 0x00000001a30edcf8 in start ()

这是 SwiftUI 中的错误吗?

【问题讨论】:

注意:MyView 应该是一个结构体 【参考方案1】:

在 macos 12.beta、xcode 13.beta、target ios 15 和 macCatalyst 上非常适合我。 在 Mac、iPhone 和 iPad 上测试。可能在旧系统上有所不同。

编辑:使用 EnvironmentObject,这是我在测试中使用的代码:

import SwiftUI

@main
struct TestApp: App 
    @StateObject var model = MyModel()  // <--- 
    var body: some Scene 
        WindowGroup 
            ContentView()
                .environmentObject(model)
        
    

struct ContentView: View 
    @EnvironmentObject var model: MyModel // <---
    var body: some View 
        NavigationView 
            MyView()
        
    

struct MyView: View 
    @EnvironmentObject var model: MyModel  // <---
    @State private var isPresentingSheet = false
    var body: some View 
        List 
            ForEach($model.vegetables)  $vegetable in
                Text(vegetable.name)
            
            .onDelete  indices in model.vegetables.remove(atOffsets: indices) 
        
        .sheet(isPresented: $isPresentingSheet) 
            Text("the sheet")  // <----
        
    

struct Vegetable: Identifiable 
    let id: UUID
    let name: String


class MyModel: ObservableObject 
    @Published var vegetables: [Vegetable] = [
        Vegetable(id: UUID(), name: "Eggplant"),
        Vegetable(id: UUID(), name: "Corn"),
        Vegetable(id: UUID(), name: "Radish")
    ]

【讨论】:

很难相信它会有所不同,但又一次。 一小时前刚刚更新到 xcode 13.0-beta5 我仍然有 xcode 13.0-beta4,和那个 xcode 一样好用。 您的回答帮助我发现了真正的问题。有关详细信息,请参阅我编辑的问题。 您的新代码对我仍然有效。有关我所做的细微更改,请参阅我的答案 EDIT。你的代码在哪一行崩溃了?

以上是关于使用 NavigationView 和工作表删除元素时 ForEach 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如何从 SwiftUI 中的 NavigationView 中删除搜索栏?

工具栏正在删除 NavigationView 中的后退按钮

NavigationView 顶部的额外空间

从 NavigationView 呈现的 SwiftUI 关闭模式表(Xcode Beta 5)

呈现工作表时 EnvironmentObject 不起作用

PageTabViewStyle 打破 NavigationView