在 SwiftUI 中符合 RandomAccessCollection 时,下标中的索引超出范围
Posted
技术标签:
【中文标题】在 SwiftUI 中符合 RandomAccessCollection 时,下标中的索引超出范围【英文标题】:Index out of range in subscript when conforming to RandomAccessCollection in SwiftUI 【发布时间】:2020-09-15 07:25:20 【问题描述】:我创建了一个类并向ObservableObject
和RandomAccessCollection
确认它,认为这个类是 Array 的自定义实现,但我不需要它是一个结构。我需要它作为一个类。
我尝试通过调用名为 append
的自定义函数将项目添加到类的内容中,并且效果很好,但是当我要删除时,由于某种原因它在 subscript
处崩溃我试图跟踪它,似乎就像每次我删除它时都会循环遍历包括已删除项目在内的所有内容,然后它会崩溃。所以我不太确定发生了什么。这是一段带有可重现代码的代码(只需复制并粘贴即可)
如果我将代码更改为 struct 它可以 100% 正常工作,但我需要它作为一个类。
Test.swift
import Foundation
public class Test<Element : Hashable>: ObservableObject
@Published fileprivate var contents:[Element] = []
public var count:Int
return self.contents.count
init()
public extension Test
func append(_ newElement: Element)
self.contents.append(newElement)
func remove(_ at: Int)
self.contents.remove(at: at)
extension Test : Collection, RandomAccessCollection
public typealias Index = Int
public typealias Indices = CountableRange<Int>
public var startIndex: Int
return self.contents.startIndex
public var endIndex: Int
return self.contents.endIndex
public subscript(position: Int) -> Element
get
return self.contents[position] // Crash occurs here after trying to remove
public func index(after i: Int) -> Int
return self.contents.index(after: i)
public func index(before i: Int) -> Int
return self.contents.index(before: i)
ContentView.swift
import SwiftUI
struct ContentView: View
@ObservedObject var test: Test<String> = Test<String>()
@State var input: String = ""
var body: some View
VStack
HStack
TextField("New Item", text: self.$input)
Button("Add", action:
if(!self.input.isEmpty)
self.test.append(self.input)
)
Button("Remove", action:
self.test.remove(0)
).disabled(self.test.count <= 0)
List
ForEach(self.test, id:\.self) n in
Text(n)
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView()
【问题讨论】:
【参考方案1】:ForEach
看不到数据的变化,因为self.test
是一个引用。
这是可能的解决方法(并且仍然有 Test 的 contents
私有)。使用 Xcode 11.7 / ios 13.7 测试
List
ForEach(Array(self.test), id:\.self) n in
Text(n)
【讨论】:
我可以看到列表随着添加代码和不添加代码而发生变化;但是,即使在进行了您提出的更改后,当我尝试删除时,我仍然会遇到Index out of range
的问题。
感谢您的回复。这确实解决了它,但我知道这是一种解决方法。我也明白ForEach
没有看到变化,但我无法理解的是为什么当我执行add
而不是remove
时它能够看到变化?另外,您是否认为目前没有黑客攻击就无法做到这一点?还是我目前在上课时所要求的是不可能的?因为上面的代码与struct
一起工作100%,但我知道结构是值类型。感谢您的帮助。【参考方案2】:
由于您超出了数组边界,您可以将集合的返回值设为可选,或者您可以在 init 中提供一个默认值,当数组为空时可以返回该值。
@ObservedObject var test: Test<String> = Test<String>(emptyValue: "")
public class Test<Element : Hashable>: ObservableObject
private let empty: Element
@Published fileprivate var contents:[Element] = []
public var count:Int
return self.contents.count
init(emptyValue: Element)
empty = emptyValue
extension Test
func append(_ newElement: Element)
self.contents.append(newElement)
func remove(_ at: Int)
self.contents.remove(at: at)
extension Test : Collection, RandomAccessCollection
public typealias Index = Int
public typealias Indices = CountableRange<Int>
public var startIndex: Int
return self.contents.startIndex
public var endIndex: Int
return self.contents.endIndex
public subscript(position: Int) -> Element
get
position < self.contents.count ? self.contents[position] : empty
public func index(after i: Int) -> Int
return self.contents.index(after: i)
public func index(before i: Int) -> Int
return self.contents.index(before: i)
【讨论】:
以上是关于在 SwiftUI 中符合 RandomAccessCollection 时,下标中的索引超出范围的主要内容,如果未能解决你的问题,请参考以下文章
在不符合 MapAnnotationProtocol 的 SwiftUI 2 中添加 MapAnnotations
ForEach 在符合 Identifiable 协议后无法在 SwiftUI 中工作
SwiftUI:类型不符合协议“UIViewRepresentable”
通用结构 'ObservedObject' 要求 'Video' 符合 SwiftUI 中的 'ObservableObject'