我在下面的一小段代码中使用swift的Data
:
var d = Data(count: 10)
d[5] = 3
let d2 = d[5..<8]
print("\(d2[0])")
令我惊讶的是,此代码在print()
上引发了异常,而以下代码没有:
var d = Data(count: 10)
d[5] = 3
let d2 = d.subdata(in: 5..<8)
print("\(d2[0])")
我以某种方式理解为什么会这样,但是我不明白为什么这样设计。当我使用subdata()
时,我得到了range的完整副本,因此从0
开始索引是有效的。但是,当我使用范围订阅[]
时,我可以访问请求的范围,而索引编制与以前相同。因此,在我的第一个示例中,d2[5]
是3
。
但是我不知道为什么要这样设计?我不想使用subdata()
方法来复制数据。我只是想通过更好的索引访问部分数据。
如果将其传递给函数,则特别会产生意外的行为。例如,以下代码创建了意外的结果和异常,您可能不容易找出原因:
func testit(idata: Data) {
if idata.count > 0 {
print("\(idata.count)")
print("\(idata[0])")
}
}
//...
var d = Data(count: 10)
d[5] = 3
let d2 = d[5..<8]
testit(idata: d2)
这段代码真的很奇怪。因为如果调试代码,您会看到print("\(idata.count)")
将3
打印为idata
的大小,这是正确的,但是用idata[0]
访问它会产生异常。
这种设计有什么理由吗?我期望我可以从订阅起始索引0访问结果Data
,但事实并非如此。我可以不使用创建数据副本的subdata()
或使用其他参数传递数据切片的基础来做到这一点吗?
答案 0 :(得分:1)
d[5..<8]
返回Data.Slice
–恰好是Data
。通常,分片与其基本集合共享索引,如Slice
中所述。
此设计决策的一个可能原因是,它保证对切片下标是O(1)操作(添加用于访问基本集合的 offset 不一定是O(1),例如不适用于字符串。)
这也很方便,例如在此示例中,在字符串中第二次出现字符后定位文本:
let string = "abcdefgabcdefg"
// Find first occurrence of "d":
if let r1 = string.range(of: "d") {
// Find second occurrence of "d":
if let r2 = string[r1.upperBound...].range(of: "d") {
print(string[r2.upperBound...]) // efg
}
}
因此,您永远不能假设集合的索引是从零开始的(除非记录,如Array.startIndex
所示)。使用startIndex
获取第一个索引,或使用first
获取第一个元素。