【问题标题】:SwiftUI List: inserting item + changing section order = app crashSwiftUI 列表:插入项目 + 更改部分顺序 = 应用程序崩溃
【发布时间】:2022-06-13 20:39:58
【问题描述】:

请看下面的代码。按下按钮一次(或最多两次)几乎肯定会使应用程序崩溃。该应用程序显示一个包含两个部分的列表,每个部分有四个项目。当按下按钮时,它会在每个部分中插入一个新项目并更改部分顺序。

我刚刚向 Apple 提交了 FB9952691。但我想知道是否有人碰巧知道 1) UIKit 是否有同样的问题?我只是好奇(我最后一次使用 UIkit 是在两年前)。 2) 是否可以在 SwiftUI 中解决该问题?谢谢。

import SwiftUI

let groupNames = (1...2).map { "\($0)" }
let groupNumber = groupNames.count

let itemValues = (1...4)
let itemNumber = itemValues.count

struct Item: Identifiable {
    var value: Int
    var id = UUID()
}

struct Group: Identifiable {
    var name: String
    var items: [Item]
    var id = UUID()
    
    // insert a random item to the group
    mutating func insertItem() {
        let index = (0...itemNumber).randomElement()!
        items.insert(Item(value: 100), at: index)
    }
}

struct Data {
    var groups: [Group]

    // initial data: 2 sections, each having 4 items.
    init() {
        groups = groupNames.map { name in
            let items = itemValues.map{ Item(value: $0) }
            return Group(name: name, items: items)
        }
    }
    
    // multiple changes: 1) reverse group order 2) insert a random item to each group
    mutating func change() {
        groups.reverse()
        for index in groups.indices {
            groups[index].insertItem()
        }
    }
}

struct ContentView: View {
    @State var data = Data()
    
    var body: some View {
        VStack {
            List {
                ForEach(data.groups) { group in
                    Section {
                        ForEach(group.items) { item in
                            Text("\(group.name): \(item.value)")
                        }
                    }
                header: {
                        Text("Section \(group.name)")
                    }
                }
            }
            Button("Press to crash the app!") {
                withAnimation {
                    data.change()
                }
            }
            .padding()
        }
    }
}

更多信息:

  1. 错误信息:

由于未捕获的异常“NSInternalInconsistencyException”而终止应用,原因:“UITableView 内部不一致:在准备批量更新时遇到超出范围的全局行索引(oldRow=8,oldGlobalRowCount=8)”

  1. 问题不是由动画引起的。删除 withAnimation 仍然有同样的问题。我认为该问题是由部分顺序更改引起的(尽管偶尔可以正常工作)。

更新:感谢@Yrb 指出insertItem() 中的一个超出索引的错误。该函数是示例代码中的设置实用程序,与change() 的问题无关。所以请忽略它。

【问题讨论】:

    标签: list swiftui crash


    【解决方案1】:

    问题出在这里:

    // multiple changes: 1) reverse group order 2) insert a random item to each group
    mutating func change() {
        groups.reverse()
        for index in groups.indices {
            groups[index].insertItem()
        }
    }
    

    您试图一次对数组做太多事情,所以在颠倒顺序的过程中,数组计数突然关闭,而 List(以及它的底层 UITableView)无法处理它。因此,您可以反转行,或向行中添加项目,但不能同时进行。

    作为奖励,这将是您的下一次崩溃:

    // insert a random item to the group
    mutating func insertItem() {
        let index = (0...itemNumber).randomElement()!
        items.insert(Item(value: 100), at: index)
    }
    

    虽然它没有导致上述问题,因为我首先修复了这个问题。您已经为itemNumber 设置了一个固定的Int,它首先是项目的计数。数组索引为 0,这意味着初始数组索引将为 (0...3)。这行let index = (0...itemNumber).randomElement()! 为您提供了一个在 (0...4) 范围内的索引,因此您有 20% 的机会在每次运行时崩溃您的应用程序。在这种情况下,始终使用 (0..

    【讨论】:

    • 感谢@Yrb 的回答。不过我有点怀疑。我认为change() 对数据模型所做的更改是“原子的”,从某种意义上说,当change() 运行中途时,SwiftUI 不会生成视图层次结构。不是吗?如果是这样,它内部的中间变化怎么会对渲染的视图层次结构可见?
    • 关于 insertItem() 的问题,是的,你是对的。但这是我的测试代码中的一个错误(我在发布它之前对代码进行了一些修改,并且在那一刻引入了它)。这只是使用 change() 重现问题的设置。但感谢您指出!
    • 即使它是原子的,视图也必须一次更新一个。这两个变化打破了这一点。简单的测试是删除一个或另一个。它工作正常。至于它究竟是如何破坏 SwiftUI 更新的,我们不知道。那是给 Apple 的 SwiftUI 团队的。另外,我并不是说这不是错误,因为它可以被处理。但是,现在您的解决方法是一次更改 1 个。
    • 是的,我知道如果我一次只执行一项更改,它工作得很好。但我认为两者一起做也应该起作用,原因有两个:1)虽然很少见,但我确实观察到当前代码偶尔会起作用 2)在实际应用程序中,通常不可能将更改分成两步。以我的应用程序为例,一个列表包含每天所有的银行账户交易。每个帐户的交易都在一个部分中。这些部分按帐户中的总交易金额排序。如果用户添加新的交易金额,可能会同时改变部分顺序。
    • OpenRadar 只是让其他人看到您报告的错误。 Apple 不使用 OpenRadar,因此您需要使用反馈助手进行报告,但这不是一个公开可见的系统。您可以根据 Apple 的回复来更新 OpenRadar 文件。
    【解决方案2】:

    我收到了 Apple 关于 FB9952691 的回复。该问题已在 iOS16 中修复(我已验证)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-05
      相关资源
      最近更新 更多