今天无聊写 swift 的时候,我写了如下代码
let entrity = [1, 2, 3, 4, 5, 6]
var slice = entrity[1...3]
slice[0] = 10
最终我发现居然崩溃了,我就纳闷了,按照我的理解,这应该是个新的数组,虽然我知道它是 ArraySlice,官方对此也只是做了解释,今天我们来分析下源码
这里声明下 Array 的 buffer 对象是 ContiguousArrayBuffer
Array 源码
@inlinable
public subscript(bounds: Range<Int>) -> ArraySlice<Element> {
get {
_checkIndex(bounds.lowerBound)
_checkIndex(bounds.upperBound)
return ArraySlice(_buffer: _buffer[bounds])
}
set(rhs) {
_checkIndex(bounds.lowerBound)
_checkIndex(bounds.upperBound)
// If the replacement buffer has same identity, and the ranges match,
// then this was a pinned in-place modification, nothing further needed.
if self[bounds]._buffer.identity != rhs._buffer.identity
|| bounds != rhs.startIndex..<rhs.endIndex {
self.replaceSubrange(bounds, with: rhs)
}
}
}
buffer[bounds]肯定调用了 subscript 方法,我们去看看这里发生了什么
ContiguousArrayBuffer 源码
@inlinable
internal subscript(bounds: Range<Int>) -> _SliceBuffer<Element> {
get {
return _SliceBuffer(
owner: _storage,
subscriptBaseAddress: firstElementAddress,
indices: bounds,
hasNativeBuffer: true)
}
set {
fatalError("not implemented")
}
}
这里生成了一个 SliceBuffer 对象用于 ArraySlice 的初始化,也就说 ArraySlice 的 buffer 其实就是 SliceBuffer.接下来我们继续回到 ArraySlice 的下标读取方法中
ArraySlice 源码
@inlinable
public subscript(index: Int) -> Element {
get {
// This call may be hoisted or eliminated by the optimizer. If
// there is an inout violation, this value may be stale so needs to be
// checked again below.
let wasNativeTypeChecked = _hoistableIsNativeTypeChecked()
// Make sure the index is in range and wasNativeTypeChecked is
// still valid.
let token = _checkSubscript(
index, wasNativeTypeChecked: wasNativeTypeChecked)
return _getElement(
index, wasNativeTypeChecked: wasNativeTypeChecked,
matchingSubscriptCheck: token)
}
_modify {
_makeMutableAndUnique() // makes the array native, too
_checkSubscript_native(index)
let address = _buffer.subscriptBaseAddress + index
yield &address.pointee
_endMutation();
}
}
看到这里,我们大家应该都知道要看_getElement 这个方法了 我们再看看这个干了什么
@inline(__always)
public // @testable
func _getElement(
_ index: Int,
wasNativeTypeChecked: Bool,
matchingSubscriptCheck: _DependenceToken
) -> Element {
#if false
return _buffer.getElement(index, wasNativeTypeChecked: wasNativeTypeChecked)
#else
return _buffer.getElement(index)
#endif
}
咦,我们一看只是很单纯的 getElement,我们继续点进去看看
SliceBuffer 源码
@inlinable
internal func getElement(_ i: Int) -> Element {
_internalInvariant(i >= startIndex, "slice index is out of range (before startIndex)")
_internalInvariant(i < endIndex, "slice index is out of range")
return subscriptBaseAddress[i]
}
好家伙,原来问题出在这里
总结
看到这里大家应该都明白了,SliceBuffer 初始化的时候有传 bounds 给它,它直接把 startIndex 设置成了 bounds 的 startIndex,所有咱们一开始写的代码直接取下标为 0 的时候,就直接崩溃了