数据范围内的订阅行为异常

时间:2019-06-16 10:32:07

标签: swift

我在下面的一小段代码中使用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()或使用其他参数传递数据切片的基础来做到这一点吗?

1 个答案:

答案 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获取第一个元素。