Data.subdata(in:) 结果为 EXC_BAD_INSTRUCTION

Data.subdata(in:) results in EXC_BAD_INSTRUCTION

在尝试检索 Data 对象的子数据时,应用程序崩溃并发出以下错误:

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

下面是代码。这是一个 Data 扩展。希望有人能解释为什么会崩溃。

public extension Data {
    /// Removes and returns the range of data at the specified position.
    /// - Parameter range: The range to remove. `range` must be valid
    /// for the collection and should not exceed the collection's end index.
    /// - Returns: The removed data.
    mutating func remove(at range: Range<Data.Index>) -> Self {
        precondition(range.lowerBound >= 0, "Range invalid, lower bound cannot be below 0")
        precondition(range.upperBound < self.count, "Range invalid, upper bound exceeds data size")
        
        let removal = subdata(in: range) // <- Error occurs here
        removeSubrange(range)
        return removal
    }
}

编辑 - 添加调用函数:

此扩展是从以下函数调用的:

func temporary(data: inout Data) -> Data {
    let _ = data.removeFirst()
    return data.remove(range: 0 ..< 3)
}

依次调用如下:

var data = Data([0,1,2,3,4,5])
let subdata = temporary(data: &data)

您没有提供足够的信息让我们知道崩溃的原因。我知道你的方法有问题的一件事是你的先决条件。您将无法通过范围来删除集合中的所有元素。除此之外,您应该实现一个通用方法,该方法采用 RangeExpression 而不是 Range。这就是我实现这种方法的方式:

extension Data {
    /// Removes and returns the range of data at the specified position.
    /// - Parameter range: The range to remove. `range` must be valid
    /// for the collection and should not exceed the collection's end index.
    /// - Returns: The removed data.
    mutating func remove<R>(_ range: R) -> Data where R: RangeExpression, Index == R.Bound {
        defer { removeSubrange(range) }
        return subdata(in: range.relative(to: self))
    }
}

用法:

var data = Data([0,1,2,3,4,5])
let subdata = data.remove(0..<6)
print(Array(data), Array(subdata))  // "[] [0, 1, 2, 3, 4, 5]\n"

要在尝试删除之前检查您的数据索引是否包含特定范围,您可以使用 pattern-matching 运算符:

var data = Data([0,1,2,3,4,5])
let range = 0..<7
if data.indices ~= range {
    let subdata = data.remove(range)
    print(Array(data), Array(subdata))
} else {
    print("invalid subrange")  // "invalid subrange\n"
}

如果你想对 ClosedRange 做同样的事情,你需要在 Range:

上实现你自己的 pattern-matching 运算符
extension Range {
    static func ~=(lhs: Self, rhs: ClosedRange<Bound>) -> Bool {
        lhs.contains(rhs.lowerBound) && lhs.contains(rhs.upperBound)
    }
}

用法:

var data = Data([0,1,2,3,4,5])
let range = 0...5
if data.indices ~= range {
    let subdata = data.remove(range)
    print(Array(data), Array(subdata))  // "[] [0, 1, 2, 3, 4, 5]\n"
} else {
    print("invalid subrange")
}

错误是由removeFirst函数引起的。文档明确指出:

Calling this method may invalidate all saved indices of this collection. Do not rely on a previously stored index value after altering a collection with any operation that can change its length.

看来这正是导致我出错的原因。我已将 removeFirst 替换为 remove(at:),现在可以使用了。