let str = "This is a swift bug"\nlet data = Data(str.utf8)\nprint("data size = ", data.endIndex, data.count)\n\nlet trimmed = data[2..<data.endIndex]\nprint("trimmed size = ", trimmed.endIndex, trimmed.count)\nRun Code Online (Sandbox Code Playgroud)\n结果是
\ndata size = 19 19\ntrimmed size = 19 17\nRun Code Online (Sandbox Code Playgroud)\n根据苹果文档endIndex:
This is the \xe2\x80\x9cone-past-the-end\xe2\x80\x9d position, and will always be equal to the count.\nRun Code Online (Sandbox Code Playgroud)\n这是一个错误吗?或者我错过了什么?
\n您应该打开 Apple 反馈以获取Data.endIndex. 这是不正确的。
数据的 startIndex 不承诺为零,这是一个不为零的示例。不幸的是,在 Data 上使用 Int 下标非常危险,除非您确切地知道 Data 是如何构造的(特别是它的索引为零)。
数据独特地混合了两个事实,这使得正确使用变得棘手:
有关对此的一些讨论以及建议的模式,请参阅Data.popFirst()、removeFirst() 调整索引。另请参阅数据范围订阅此问题的另一个版本的奇怪行为。
array[2..<array.endIndex]当您使用类似于创建切片的表达式时。切片是数组(或类似于数组的东西)的一种窗口。它startIndex不一定是原始索引的最后一个索引之后的索引0。endIndex
例子:
\nlet arr = Array(1...10)\nprint(arr.startIndex) // 0\nprint(arr.endIndex) // 10\nlet slice = arr[2...4]\nprint(slice.startIndex) // 2\nprint(slice.endIndex) // 5\nprint(slice.count) // 3\nRun Code Online (Sandbox Code Playgroud)\n你明白这是如何运作的吗?切片有自己的逻辑。它的大小(计数)是切片的大小,但它的索引号来自原始数组,因为切片只不过是指向原始数组的一部分的指针。它没有独立的存在;可以说,这只是一种观察方式。
\n一个重要的后果是会崩溃:正如我们已经被告知的,slice[0]第一个可用索引是 2。slice这就是为什么知道您正在处理原始数组还是切片至关重要。
但是,至少您有理由知道这个问题可能存在,因为slice有一个特殊类型 \xe2\x80\x94 Array<Int>.SubSequence,意味着 ArraySlice 。但是,您通过数据遇到这种情况的事实使其变得更加棘手,因为它trimmed是作为数据键入的,而不是作为 DataSlice 键入的!它实际上是Data.SubSequencea ,但是您没有简单的方法可以找到它!那是因为Data.SubSequence对数据本身进行了类型别名。这被视为数据实现中的缺陷。
然而,这是完全相同的现象。这些答案看起来应该很熟悉:
\nlet str = "This is a swift bug"\nlet data = Data(str.utf8)\nlet trimmed = data[2...4]\nprint(trimmed.startIndex) // 2\nprint(trimmed.endIndex) // 5\nprint(trimmed.count) // 3\nRun Code Online (Sandbox Code Playgroud)\n解决这个问题的最好方法是“不要这样做”。要将数据的子范围作为真实数据,请使用subdata:
let trimmed2 = data.subdata(in: 2..<5)\nprint(trimmed2.startIndex) // 0, and so on; it's an independent copy\nRun Code Online (Sandbox Code Playgroud)\n