「这是我参与2022初次更文应战的第27天,活动详情检查:2022初次更文应战」。
1. 数组的组成
咱们界说一个number的数组,并把它编译成SIL文件
var number = [1,2,3,4,5,6]
print(number)
编译SIL文件
首要的是关注初始化的数组的时分会调用_allocateUninitializedArray
办法,之后存储咱们的值。咱们能够在源码中大局搜索下这个办法能够得到:
咱们就在 ArrayShared.swift
中找到这个函数的详细完成
@inlinable // FIXME(inline-always)
@inline(__always)
@_semantics("array.uninitialized_intrinsic")
public // COMPILER_INTRINSIC
func _allocateUninitializedArray<Element>(_ builtinCount: Builtin.Word)
-> (Array<Element>, Builtin.RawPointer) {
let count = Int(builtinCount)
if count > 0 {
// Doing the actual buffer allocation outside of the array.uninitialized
// semantics function enables stack propagation of the buffer.
let bufferObject = Builtin.allocWithTailElems_1(
_ContiguousArrayStorage<Element>.self, builtinCount, Element.self)
let (array, ptr) = Array<Element>._adoptStorage(bufferObject, count: count)
return (array, ptr._rawValue)
}
// For an empty array no buffer allocation is needed.
let (array, ptr) = Array<Element>._allocateUninitialized(count)
return (array, ptr._rawValue)
}
能够发现首先判断当时的数组count
是否大于0,不大于0则创立一个空的数组。大于0则经过allocWithTailElems_1
的办法在堆区创立一个内存空间来寄存数组中的元素。之后经过 _adoptStorage
来创立数组,咱们来看一下_adoptStorage
办法内部是怎么完成的,
咱们在Array.swift
中查找该办法
@inlinable
@_semantics("array.uninitialized")
internal static func _adoptStorage(
_ storage: __owned _ContiguousArrayStorage<Element>, count: Int
) -> (Array, UnsafeMutablePointer<Element>) {
let innerBuffer = _ContiguousArrayBuffer<Element>(
count: count,
storage: storage)
return (
Array(
_buffer: _Buffer(_buffer: innerBuffer, shiftedToStartIndex: 0)),
innerBuffer.firstElementAddress)
}
创立一个buffer
,用来存储咱们的数组中的元素。之后回来一个元组
,包含这个buffer
和buffer的首地址
。因此咱们能够发现这个元组会回来咱们创立的数组和数组的首元素地址。
是因为在第一个元素的地址之前,应该还有其它的信息。这儿先给出结论,如图:
这儿简单的介绍下,数组存储的其实是一个名为_ContiguousArrayStorage
类的内存地址,而这个类存储这个存储着 元类型
,引用计数
,元素的个数
,容量的巨细和标志位
,以及首个元素的内存地址
。
我么使用LLDB调试下:
经过调试也验证了上面咱们结论的分布状况。
2. 数组拼接
咱们通常时分append
进行增加,number.append(7)。那么此时的数组应该要扩容,咱们检查源码关于append的完成。
@_semantics("array.append_element")
public mutating func append(_ newElement: __owned Element) {
// Separating uniqueness check and capacity check allows hoisting the
// uniqueness check out of a loop.
_makeUniqueAndReserveCapacityIfNotUnique()
let oldCount = _buffer.mutableCount
_reserveCapacityAssumingUniqueBuffer(oldCount: oldCount)
_appendElementAssumeUniqueAndCapacity(oldCount, newElement: newElement)
_endMutation()
}
咱们持续看下_makeUniqueAndReserveCapacityIfNotUnique
的完成
@inlinable
@_semantics("array.make_mutable")
internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() {
if _slowPath(!_buffer.beginCOWMutation()) {
_createNewBuffer(bufferIsUnique: false,
minimumCapacity: count &+ 1,
growForAppend: true)
}
}
如果缓冲区的存储是唯一引用的,将缓冲区置于可变状况,然后去创立新的 buffer
。咱们持续看这个_createNewBuffer(bufferIsUnique:minimumCapacity:growForAppend)
的完成,代码如下:
/// Copy the contents of the current buffer to a new unique mutable buffer.
/// The count of the new buffer is set to `oldCount`, the capacity of the
/// new buffer is big enough to hold 'oldCount' + 1 elements.
@inline(never)
@inlinable // @specializable
internal mutating func _copyToNewBuffer(oldCount: Int) {
let newCount = oldCount &+ 1
var newBuffer = _buffer._forceCreateUniqueMutableBuffer(
countForNewBuffer: oldCount, minNewCapacity: newCount)
_buffer._arrayOutOfPlaceUpdate(&newBuffer, oldCount, 0)
}
在这个办法当中它会去调用 _growArrayCapacity
办法来进行扩容,咱们持续来看 _growArrayCapacity 的完成
每次扩容是原有基础的2倍
。关于咱们的数组来说,当需求扩容的时分,进行2倍扩容
而不是仅仅扩容咱们需求的内存
,为了避免下次再次增加时能够不扩容,尽管可能会糟蹋一定的空间,可是提高了效率。