「这是我参与2022初次更文应战的第27天,活动详情检查:2022初次更文应战」。

1. 数组的组成

咱们界说一个number的数组,并把它编译成SIL文件

var number = [1,2,3,4,5,6]
print(number)

编译SIL文件

Swift进阶-23-Swift中Array探究

首要的是关注初始化的数组的时分会调用_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,用来存储咱们的数组中的元素。之后回来一个元组,包含这个bufferbuffer的首地址。因此咱们能够发现这个元组会回来咱们创立的数组和数组的首元素地址。
是因为在第一个元素的地址之前,应该还有其它的信息。这儿先给出结论,如图:

Swift进阶-23-Swift中Array探究

这儿简单的介绍下,数组存储的其实是一个名为_ContiguousArrayStorage类的内存地址,而这个类存储这个存储着 元类型引用计数元素的个数,容量的巨细和标志位,以及首个元素的内存地址

我么使用LLDB调试下:

Swift进阶-23-Swift中Array探究

经过调试也验证了上面咱们结论的分布状况。

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 的完成

Swift进阶-23-Swift中Array探究

每次扩容是原有基础的2倍。关于咱们的数组来说,当需求扩容的时分,进行2倍扩容而不是仅仅扩容咱们需求的内存,为了避免下次再次增加时能够不扩容,尽管可能会糟蹋一定的空间,可是提高了效率。