致命错误:在特定视图上刷新数据时索引超出范围

Posted

技术标签:

【中文标题】致命错误:在特定视图上刷新数据时索引超出范围【英文标题】:Fatal error: Index out of range when refreshing data on specific view 【发布时间】:2020-07-30 12:24:08 【问题描述】:

我有以下代码:

import SwiftUI
import SwiftUIRefresh // added via SwiftPM: https://github.com/timbersoftware/SwiftUIRefresh.git

struct AView: View 
    
    @State private var aObjects = [A]()
    @State private var refreshing = false
    
    var body: some View 
        NavigationView 
            VStack 
                if(aObjects.isEmpty) 
                    Text("loading")
                 else 
                    List(aObjects.indices, id: \.self)  idx in
                        NavigationLink(destination: BView(parentA: self.$aObjects[idx])) 
                            Text(self.aObjects[idx].name)
                        
                    .pullToRefresh(isShowing: self.$refreshing) 
                        API.getAs()  newAObjects in
                            self.aObjects = newAObjects
                            self.refreshing = false
                        
                    
                
            .navigationBarTitle("All", displayMode: .large)
            
        .onAppear() 
            if(self.aObjects.isEmpty) 
                API.getAs()  newAObjects in
                    self.aObjects = newAObjects
                
            
        
    


struct BView: View 
    
    @Binding var parentA : A
    
    @State private var refreshing = false
    
    var body: some View 
        VStack 
            if(parentA.bs.isEmpty) 
                Text("loading")
             else 
                List(parentA.bs.indices, id: \.self)  idx in
                    NavigationLink(destination: CView(parentA: self.$parentA, parentB: self.$parentA.bs[idx])) 
                        Text(self.parentA.bs[idx].name)
                    
                .pullToRefresh(isShowing: self.$refreshing) 
                    API.getBs(forA: self.parentA)  newBObjects in
                        self.parentA.bs = newBObjects
                        self.refreshing = false
                    
                
            
        
        .navigationBarTitle(Text(parentA.name), displayMode: .inline)
        .onAppear() 
            if(self.parentA.bs.isEmpty) 
                API.getBs(forA: self.parentA)  newBObjects in
                    self.parentA.bs = newBObjects
                
            
        
    


struct CView: View 
    
    @Binding var parentA : A
    @Binding var parentB : B
    
    @State private var refreshing = false
    
    var body: some View 
            VStack 
                if(parentB.cs.isEmpty) 
                    Text("loading")
                 else 
                    List(parentB.cs.indices, id: \.self)  idx in
                        Text(self.parentB.cs[idx].name + self.parentB.cs[idx].description)
                    .pullToRefresh(isShowing: self.$refreshing) 
                        API.getCs(forB: self.parentB, inA: self.parentA)  newCObjects in
                            self.parentB.cs = newCObjects
                            self.refreshing = false
                        
                    
                
            
            .navigationBarTitle(Text(parentB.name), displayMode: .inline)
            .onAppear() 
                if(self.parentB.cs.isEmpty) 
                    API.getCs(forB: self.parentB, inA: self.parentA)  newCObjects in
                        self.parentB.cs = newCObjects
                    
                
            
    



class API 
    
    static func getAs(complete : @escaping ([A]) -> Void) 
        // normaly download from server
        let aobjects = [A(id: 1, name: "1"), A(id: 2, name: "2"), A(id: 3, name: "3")]
        complete(aobjects)
    
    
    static func getBs(forA : A, complete: @escaping ([B]) -> Void) 
        // normaly download all bs for a provided A from server
        let bObjects = [B(id: 4, name: "4"), B(id: 5, name: "5")]
        complete(bObjects)
    
    
    static func getCs(forB : B, inA: A, complete: @escaping ([C]) -> Void) 
        // normaly download all cs for a provided B in parent A from server
        let cObjects = [C(id: 6, name: "name", description: "desc")]
        complete(cObjects)
    


struct A 
    public let id : Int
    public let name : String
    public var bs = [B]()
    
    init(id: Int, name: String) 
        self.id = id
        self.name = name
    


struct B 
    public let id : Int
    public let name : String
    public var cs = [C]()
    
    init(id: Int, name: String) 
        self.id = id
        self.name = name
    


struct C 
    public let id : Int
    public let name : String
    public let description : String
    
    init(id: Int, name: String, description : String) 
        self.id = id
        self.name = name
        self.description = description
    

到目前为止,这有效。用户可以从 AView 导航到 BView 再到 CView。如果用户执行以下操作,则会出现标题中的问题和错误:从 AView 开始,转到 BView,转到 CView,返回 BView,返回 AView,通过下拉手势刷新。在其他情况下,它不会发生,例如。从 AView 开始,转到 BView,转到 CView,返回 BView,返回 AView,转到另一个 BView,返回 AView, 通过下拉手势刷新,这样可以。知道如何防止异常吗?

提前致谢

编辑:完整的错误消息

致命错误:索引超出范围:文件 /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1103.2.25.8/swift/stdlib/public/core/ContiguousArrayBuffer.swift,第 444 行 2020-07-30 14:16:37.652919+0200 TestForArray[11908:625819] 致命错误:索引超出范围:文件 /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1103.2.25.8/swift/ stdlib/public/core/ContiguousArrayBuffer.swift,第 444 行

【问题讨论】:

它是在“正常”条件下(新数据到达时)失败,还是在固定刷新数据时也失败,就像问题中一样 【参考方案1】:

您试图从超出数组范围的索引中获取数据。您可以使用此扩展程序。它将帮助您安全地获取对象。 https://github.com/MahmoudMMB/MahmoudMMB-Fetching_an_item_from_array_safely/blob/main/SafeFetching.swift

【讨论】:

不允许仅链接答案。链接会随着时间而中断。如果链接中有相关代码,请将代码贴在答案中 您应该观看 WWDC21 中的 Demystify SwiftUI,因此 Apple 认为这种特定做法是不安全的,应该避免 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于致命错误:在特定视图上刷新数据时索引超出范围的主要内容,如果未能解决你的问题,请参考以下文章

致命错误:单击特定单元格进行 segue 时索引超出范围

CollectionView - 致命错误:索引超出范围 - 比视图显示的单元格多

表视图错误致命错误:加载文件时索引超出范围

使用致命错误刷新tableview时应用程序崩溃:索引超出范围Swift3

从数组中删除 - 致命错误:索引超出范围 - SwiftUI 绑定

SwiftUI - 致命错误:从数组中删除元素时索引超出范围