Swift底層進(jìn)階--019:Array源碼解析

  • Array使用有序列表存儲(chǔ)同一類型的多個(gè)值。相同的值可以多次出現(xiàn)在一個(gè)數(shù)組的不同位置中。
  • Array會(huì)強(qiáng)制檢測(cè)元素的類型,如果類型不同則會(huì)報(bào)錯(cuò)。Swift數(shù)組應(yīng)該遵循像Array<Element>這樣的形式,其中Element是這個(gè)數(shù)組中唯一允許存在的數(shù)據(jù)類型。
  • 如果創(chuàng)建一個(gè)數(shù)組,并賦值給一個(gè)變量,則創(chuàng)建的集合就是可以修改的。這意味著在創(chuàng)建數(shù)組后,可以通過(guò)添加、刪除、修改的方式改變數(shù)組里的元素。
  • 如果將一個(gè)數(shù)組賦值給常量,數(shù)組就不可更改,并且數(shù)組的大小和內(nèi)容都不可以修改。
Array的創(chuàng)建

Array有很多創(chuàng)建?式:

通過(guò)字面量方式創(chuàng)建一個(gè)Int類型數(shù)組

var numbers = [1, 2, 3, 4, 5, 6]

通過(guò)字面量方式創(chuàng)建一個(gè)String類型數(shù)組

var strArray = ["Hank", "CC", "Cooci", "Cat", "Kody"]

也可以使用Array()初始化方法創(chuàng)建數(shù)組,但這種方式必須指明數(shù)據(jù)類型,否則編譯報(bào)錯(cuò)

使用Array()方法創(chuàng)建數(shù)組

var emptyArray: [Int] = Array()

這種寫法,等同于上述方式

var emptyArray: Array<Int> = Array()

當(dāng)訪問(wèn)數(shù)組的元素不存在時(shí),運(yùn)行時(shí)會(huì)報(bào)錯(cuò):數(shù)組越界(Index out of range

如果想在數(shù)組創(chuàng)建時(shí)給定初始值,可以使用Array(repeating:, count:)方法

var emptyArray = Array(repeating: 0, count: 10)

得到10個(gè)長(zhǎng)度的數(shù)組,初始值全部為0

分析SIL代碼

通過(guò)SIL代碼分析數(shù)組的創(chuàng)建過(guò)程

var numbers = [1, 2, 3]

將上述代碼生成SIL文件:swiftc -emit-sil main.swift | xcrun swift-demangle

// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  alloc_global @main.numbers : [Swift.Int]           // id: %2
  %3 = global_addr @main.numbers : [Swift.Int] : $*Array<Int> // user: %23
  %4 = integer_literal $Builtin.Word, 3           // user: %6
  // function_ref _allocateUninitializedArray<A>(_:)
  %5 = function_ref @Swift._allocateUninitializedArray<A>(Builtin.Word) -> ([A], Builtin.RawPointer) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %6
  %6 = apply %5<Int>(%4) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // users: %8, %7
  %7 = tuple_extract %6 : $(Array<Int>, Builtin.RawPointer), 0 // user: %23
  %8 = tuple_extract %6 : $(Array<Int>, Builtin.RawPointer), 1 // user: %9
  %9 = pointer_to_address %8 : $Builtin.RawPointer to [strict] $*Int // users: %12, %19, %14
  %10 = integer_literal $Builtin.Int64, 1         // user: %11
  %11 = struct $Int (%10 : $Builtin.Int64)        // user: %12
  store %11 to %9 : $*Int                         // id: %12
  %13 = integer_literal $Builtin.Word, 1          // user: %14
  %14 = index_addr %9 : $*Int, %13 : $Builtin.Word // user: %17
  %15 = integer_literal $Builtin.Int64, 2         // user: %16
  %16 = struct $Int (%15 : $Builtin.Int64)        // user: %17
  store %16 to %14 : $*Int                        // id: %17
  %18 = integer_literal $Builtin.Word, 2          // user: %19
  %19 = index_addr %9 : $*Int, %18 : $Builtin.Word // user: %22
  %20 = integer_literal $Builtin.Int64, 3         // user: %21
  %21 = struct $Int (%20 : $Builtin.Int64)        // user: %22
  store %21 to %19 : $*Int                        // id: %22
  store %7 to %3 : $*Array<Int>                   // id: %23
  %24 = integer_literal $Builtin.Int32, 0         // user: %25
  %25 = struct $Int32 (%24 : $Builtin.Int32)      // user: %26
  return %25 : $Int32                             // id: %26
} // end sil function 'main'
  • %5:通過(guò)_allocateUninitializedArray函數(shù)進(jìn)行初始化
  • %7:從元組中獲取第0個(gè)元素,拿到一個(gè)Array
  • %8:從元組中獲取第1個(gè)元素,拿到一個(gè)地址
  • %9:對(duì)%8的地址引用,*Int類型
  • %10、%11:創(chuàng)建字面量1
  • store %11 to %9:將字面量1存儲(chǔ)到%9
  • %13:地址偏移
  • %14:給定引用值數(shù)組的地址,返回?cái)?shù)組中index=1位置的地址
  • 下面以此類推,將剩余元素依次放入到連續(xù)的存儲(chǔ)空間中
源碼分析

Array應(yīng)該是?個(gè)值類型,但SIL代碼中卻出現(xiàn)了_alloc函數(shù)的調(diào)用,通過(guò)源碼分析,看一下它在底層到底做了什么

打開ArrayShared.swift文件,找到_allocateUninitializedArray的定義:

@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)
}
  • 如果進(jìn)入count > 0的條件分支
  • 使用_allocateUninitialized函數(shù)分配堆內(nèi)存,先創(chuàng)建_ContiguousArrayStorage類,并在_ContiguousArrayStorage類的尾部,分配builtinCount元素大小的連續(xù)內(nèi)存空間,存放Element.self
  • 調(diào)用Array_adoptStorage函數(shù)
  • 返回一個(gè)元組類型

_allocateUninitialized函數(shù)內(nèi)部調(diào)用了HeapObject.cpp文件中的swift_allocObject函數(shù)

HeapObject *swift::swift_allocObject(HeapMetadata const *metadata,
                                     size_t requiredSize,
                                     size_t requiredAlignmentMask) {
  CALL_IMPL(swift_allocObject, (metadata, requiredSize, requiredAlignmentMask));
}

打開Array.swift文件,找到_adoptStorage的定義:

  @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)
  }
  • 通過(guò)_ContiguousArrayBuffer函數(shù)創(chuàng)建innerBuffer私有變量
  • 返回元組類型
  • Array(_buffer: _Buffer(_buffer: , shiftedToStartIndex: ))Array的實(shí)例對(duì)象
  • innerBuffer.firstElementAddress:當(dāng)前元素的首地址

找到_Buffer的定義

@frozen
public struct Array<Element>: _DestructorSafeContainer {
  #if _runtime(_ObjC)
  @usableFromInline
  internal typealias _Buffer = _ArrayBuffer<Element>
  #else
  @usableFromInline
  internal typealias _Buffer = _ContiguousArrayBuffer<Element>
  #endif

  @usableFromInline
  internal var _buffer: _Buffer

  /// Initialization from an existing buffer does not have "array.init"
  /// semantics because the caller may retain an alias to buffer.
  @inlinable
  internal init(_buffer: _Buffer) {
    self._buffer = _buffer
  }
}
  • 如果是和ObjC交互,返回_ArrayBuffer,否則返回_ContiguousArrayBuffer

所以在上面SIL代碼中的%8,拿到的就是元素的首地址

打開ContiguousArrayBuffer.swift文件,找到_ContiguousArrayBuffer的定義:

@usableFromInline
@frozen
internal struct _ContiguousArrayBuffer<Element>: _ArrayBufferProtocol {

  /// Make a buffer with uninitialized elements.  After using this
  /// method, you must either initialize the `count` elements at the
  /// result's `.firstElementAddress` or set the result's `.count`
  /// to zero.
  @inlinable
  internal init(
    _uninitializedCount uninitializedCount: Int,
    minimumCapacity: Int
  ) {
    let realMinimumCapacity = Swift.max(uninitializedCount, minimumCapacity)
    if realMinimumCapacity == 0 {
      self = _ContiguousArrayBuffer<Element>()
    }
    else {
      _storage = Builtin.allocWithTailElems_1(
         _ContiguousArrayStorage<Element>.self,
         realMinimumCapacity._builtinWordValue, Element.self)

      let storageAddr = UnsafeMutableRawPointer(Builtin.bridgeToRawPointer(_storage))
      let endAddr = storageAddr + _swift_stdlib_malloc_size(storageAddr)
      let realCapacity = endAddr.assumingMemoryBound(to: Element.self) - firstElementAddress

      _initStorageHeader(
        count: uninitializedCount, capacity: realCapacity)
    }
  }
  • _ContiguousArrayBuffer也是一個(gè)結(jié)構(gòu)體
  • 里面只有一個(gè)_storage變量

找到init的定義:

  @inlinable
  internal init(count: Int, storage: _ContiguousArrayStorage<Element>) {
    _storage = storage

    _initStorageHeader(count: count, capacity: count)
  }
  • init方法中,_storage存儲(chǔ)的是alloc出來(lái)的堆上的內(nèi)存

找到_storage的定義:

  @usableFromInline
  internal var _storage: __ContiguousArrayStorageBase
  • _storage是一個(gè)__ContiguousArrayStorageBase類型變量

打開SwiftNativeNSArray.swift文件,找到__ContiguousArrayStorageBase的定義:

@usableFromInline
@_fixed_layout
internal class __ContiguousArrayStorageBase
  : __SwiftNativeNSArrayWithContiguousStorage {

  @usableFromInline
  final var countAndCapacity: _ArrayBody

  @inlinable
  @nonobjc
  internal init(_doNotCallMeBase: ()) {
    _internalInvariantFailure("creating instance of __ContiguousArrayStorageBase")
  }
  
#if _runtime(_ObjC)
  internal override func withUnsafeBufferOfObjects<R>(
    _ body: (UnsafeBufferPointer<AnyObject>) throws -> R
  ) rethrows -> R {
    if let result = try _withVerbatimBridgedUnsafeBuffer(body) {
      return result
    }
    _internalInvariantFailure(
      "Can't use a buffer of non-verbatim-bridged elements as an NSArray")
  }

  /// If the stored type is bridged verbatim, invoke `body` on an
  /// `UnsafeBufferPointer` to the elements and return the result.
  /// Otherwise, return `nil`.
  internal func _withVerbatimBridgedUnsafeBuffer<R>(
    _ body: (UnsafeBufferPointer<AnyObject>) throws -> R
  ) rethrows -> R? {
    _internalInvariantFailure(
      "Concrete subclasses must implement _withVerbatimBridgedUnsafeBuffer")
  }

  internal func _getNonVerbatimBridgingBuffer() -> _BridgingBuffer {
    _internalInvariantFailure(
      "Concrete subclasses must implement _getNonVerbatimBridgingBuffer")
  }
  
  @objc(mutableCopyWithZone:)
  dynamic internal func mutableCopy(with _: _SwiftNSZone?) -> AnyObject {
    let arr = Array<AnyObject>(_ContiguousArrayBuffer(self))
    return _SwiftNSMutableArray(arr)
  }
  
  @objc(indexOfObjectIdenticalTo:)
  dynamic internal func index(ofObjectIdenticalTo object: AnyObject) -> Int {
    let arr = Array<AnyObject>(_ContiguousArrayBuffer(self))
    return arr.firstIndex { $0 === object } ?? NSNotFound
  }
#endif

@inlinable
  internal func canStoreElements(ofDynamicType _: Any.Type) -> Bool {
    _internalInvariantFailure(
      "Concrete subclasses must implement canStoreElements(ofDynamicType:)")
  }

  /// A type that every element in the array is.
  @inlinable
  internal var staticElementType: Any.Type {
    _internalInvariantFailure(
      "Concrete subclasses must implement staticElementType")
  }
  
  @inlinable
  deinit {
    _internalInvariant(
      self !== _emptyArrayStorage, "Deallocating empty array storage?!")
  }
}
  • __ContiguousArrayStorageBase是一個(gè)Class類型
  • 有一個(gè)_ArrayBody類型的成員屬性countAndCapacity

打開ContiguousArrayBuffer.swift文件,找到_initStorageHeader的定義:

  @inlinable
  internal func _initStorageHeader(count: Int, capacity: Int) {
#if _runtime(_ObjC)
    let verbatim = _isBridgedVerbatimToObjectiveC(Element.self)
#else
    let verbatim = false
#endif

    // We can initialize by assignment because _ArrayBody is a trivial type,
    // i.e. contains no references.
    _storage.countAndCapacity = _ArrayBody(
      count: count,
      capacity: capacity,
      elementTypeIsBridgedVerbatim: verbatim)
  }
  • _initStorageHeader函數(shù)內(nèi)調(diào)用_ArrayBody函數(shù),賦值給_storagecountAndCapacity屬性

打開ArrayBody.swift文件,找到_ArrayBody的定義:

@frozen
@usableFromInline
internal struct _ArrayBody {
  @usableFromInline
  internal var _storage: _SwiftArrayBodyStorage

  @inlinable
  internal init(
    count: Int, capacity: Int, elementTypeIsBridgedVerbatim: Bool = false
  ) {
    _internalInvariant(count >= 0)
    _internalInvariant(capacity >= 0)
    
    _storage = _SwiftArrayBodyStorage(
      count: count,
      _capacityAndFlags:
        (UInt(truncatingIfNeeded: capacity) &<< 1) |
        (elementTypeIsBridgedVerbatim ? 1 : 0))
  }

  /// In principle ArrayBody shouldn't need to be default
  /// constructed, but since we want to claim all the allocated
  /// capacity after a new buffer is allocated, it's typical to want
  /// to update it immediately after construction.
  @inlinable
  internal init() {
    _storage = _SwiftArrayBodyStorage(count: 0, _capacityAndFlags: 0)
  }
  
  /// The number of elements stored in this Array.
  @inlinable
  internal var count: Int {
    get {
      return _assumeNonNegative(_storage.count)
    }
    set(newCount) {
      _storage.count = newCount
    }
  }

  /// The number of elements that can be stored in this Array without
  /// reallocation.
  @inlinable
  internal var capacity: Int {
    return Int(_capacityAndFlags &>> 1)
  }

  /// Is the Element type bitwise-compatible with some Objective-C
  /// class?  The answer is---in principle---statically-knowable, but
  /// I don't expect to be able to get this information to the
  /// optimizer before 1.0 ships, so we store it in a bit here to
  /// avoid the cost of calls into the runtime that compute the
  /// answer.
  @inlinable
  internal var elementTypeIsBridgedVerbatim: Bool {
    get {
      return (_capacityAndFlags & 0x1) != 0
    }
    set {
      _capacityAndFlags
        = newValue ? _capacityAndFlags | 1 : _capacityAndFlags & ~1
    }
  }

  /// Storage optimization: compresses capacity and
  /// elementTypeIsBridgedVerbatim together.
  @inlinable
  internal var _capacityAndFlags: UInt {
    get {
      return _storage._capacityAndFlags
    }
    set {
      _storage._capacityAndFlags = newValue
    }
  }
}
  • _ArrayBody是一個(gè)結(jié)構(gòu)體
  • _ArrayBody結(jié)構(gòu)體中,包含一個(gè)_SwiftArrayBodyStorage類型的_storage變量

打開GlobalObjects.h文件,找到_SwiftArrayBodyStorage的定義:

struct _SwiftArrayBodyStorage {
  __swift_intptr_t count;
  __swift_uintptr_t _capacityAndFlags;
};
  • _SwiftArrayBodyStorage也是一個(gè)結(jié)構(gòu)體
  • 包含count_capacityAndFlags兩個(gè)成員屬性
Array內(nèi)存布局

現(xiàn)在梳理一下Array的內(nèi)存布局:

Struct Array->Struct _ContiguousArrayBuffer->Class __ContiguousArrayStorageBase-> 包含?個(gè)Struct ArrayBody屬性 -> 包含?個(gè)struct _SwiftArrayBodyStorage屬性 -> 包含count_capacityAndFlags兩個(gè)屬性

這??致推導(dǎo)出Array的內(nèi)存布局,其實(shí)最主要和__ContiguousArrayStorageBase類有關(guān),因?yàn)橹暗亩际侵殿愋?/p>

通過(guò)LLDB調(diào)試來(lái)驗(yàn)證?下:

定義numbers數(shù)組,初始化1、2、3三個(gè)元素

var numbers = [1, 2, 3]

通過(guò)withUnsafePointer打印numbers內(nèi)存地址

通過(guò)x/8g查看numbers的內(nèi)存地址,里面包含了一個(gè)堆上的地址0x0000000100410830

通過(guò)x/8g查看內(nèi)存地址0x0000000100410830

  • 0x00007fff995404d8:元類型metaData,通過(guò)cat address打印,它存儲(chǔ)在__DATA.__bss段,即:未初始化段
  • 0x0000000200000002:引用計(jì)數(shù)refCount
  • 0x0000000000000003:數(shù)組大小count,這里輸出為3
  • 0x0000000000000006:容量_capacityAndFlags,這里輸出為6。這是什么情況?難道容量是數(shù)組大小的2倍嗎?
  • 后面三位分別是數(shù)組內(nèi)元素1、23

通過(guò)po打印numbers,由于返回的是元素首地址,所以直接輸出的是值,而不是Array的內(nèi)存數(shù)據(jù)結(jié)構(gòu)

Array的容量(capacity)

numbers數(shù)組的大小為3,為什么容量卻是6,難道容量是數(shù)組大小的2倍

打開ArrayBody.swift文件,在_ArrayBody結(jié)構(gòu)體內(nèi),找到對(duì)_storage變量進(jìn)行賦值的代碼:

_storage = _SwiftArrayBodyStorage(
      count: count,
      _capacityAndFlags:
        (UInt(truncatingIfNeeded: capacity) &<< 1) |
        (elementTypeIsBridgedVerbatim ? 1 : 0))
  • _capacityAndFlags容量被賦值時(shí),對(duì)capacity進(jìn)行了&運(yùn)算的操作
  • elementTypeIsBridgedVerbatim用來(lái)判斷當(dāng)前元素是否允許橋接ObjC

在上面的案例中,numbers數(shù)組的容量應(yīng)該為3,經(jīng)過(guò)&運(yùn)算操作輸出為6

  • 本質(zhì)上Array的容量還是3
Array不同創(chuàng)建?式的差別

Array不同的創(chuàng)建?式,導(dǎo)致Metadata有所差別

使用字面量方式或者通過(guò)Array(repeating:, count:)方法創(chuàng)建數(shù)組

  • Metadata打印為InitialAllocationPool,存儲(chǔ)在__DATA.__bss

使用Array()方法創(chuàng)建數(shù)組

  • Metadata打印的類型是_swiftEmptyArrayStorage,存儲(chǔ)在__DATA.__data

打開GlobalObjects.h文件,找到_SwiftEmptyArrayStorage的定義:

struct _SwiftEmptyArrayStorage {
  struct HeapObject header;
  struct _SwiftArrayBodyStorage body;
};
  • _SwiftEmptyArrayStorage是一個(gè)結(jié)構(gòu)體,里面包含HeapObject結(jié)構(gòu)體和_SwiftArrayBodyStorage結(jié)構(gòu)體
Array的數(shù)據(jù)的拼接

當(dāng)Array進(jìn)行append操作時(shí),肯定會(huì)涉及到數(shù)組的擴(kuò)容

var numbers = [1, 2, 3, 4, 5, 6]
numbers.append(7)

對(duì)于數(shù)組的擴(kuò)容,底層是如何處理的?

打開Array.swift文件,找到append的定義:

  @inlinable
  @_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 = _getCount()
    _reserveCapacityAssumingUniqueBuffer(oldCount: oldCount)
    _appendElementAssumeUniqueAndCapacity(oldCount, newElement: newElement)
  }
  • _makeUniqueAndReserveCapacityIfNotUnique:判斷當(dāng)前Array是否有唯一的引用計(jì)數(shù)
  • _reserveCapacityAssumingUniqueBuffer:將數(shù)組進(jìn)行反轉(zhuǎn)
  • _appendElementAssumeUniqueAndCapacityappend元素到Array

找到_makeUniqueAndReserveCapacityIfNotUnique的定義:

  @inlinable
  @_semantics("array.make_mutable")
  internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() {
    if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) {
      _createNewBuffer(bufferIsUnique: false,
                       minimumCapacity: count + 1,
                       growForAppend: true)
    }
  }
  • isMutableAndUniquelyReferenced:判斷是否存在多個(gè)引用計(jì)數(shù)
  • 如果存在多個(gè),調(diào)用_createNewBuffer函數(shù)觸發(fā)寫時(shí)復(fù)制

找到_createNewBuffer的定義:

  @_alwaysEmitIntoClient
  @inline(never)
  internal mutating func _createNewBuffer(
    bufferIsUnique: Bool, minimumCapacity: Int, growForAppend: Bool
  ) {
    let newCapacity = _growArrayCapacity(oldCapacity: _getCapacity(),
                                         minimumCapacity: minimumCapacity,
                                         growForAppend: growForAppend)
    let count = _getCount()
    _internalInvariant(newCapacity >= count)
    
    let newBuffer = _ContiguousArrayBuffer<Element>(
      _uninitializedCount: count, minimumCapacity: newCapacity)

    if bufferIsUnique {
      _internalInvariant(_buffer.isUniquelyReferenced())

      // As an optimization, if the original buffer is unique, we can just move
      // the elements instead of copying.
      let dest = newBuffer.firstElementAddress
      dest.moveInitialize(from: _buffer.firstElementAddress,
                          count: count)
      _buffer.count = 0
    } else {
      _buffer._copyContents(
        subRange: 0..<count,
        initializing: newBuffer.firstElementAddress)
    }
    _buffer = _Buffer(_buffer: newBuffer, shiftedToStartIndex: 0)
  }
  • _growArrayCapacity:獲取到數(shù)組擴(kuò)容后的容量
  • 如果bufferIsUniquetrue,表示引用計(jì)數(shù)唯一,調(diào)用moveInitialize函數(shù),將數(shù)組元素移動(dòng)到新的緩沖區(qū),而不是復(fù)制
  • 如果為false,表示存在多個(gè)引用計(jì)數(shù),調(diào)用_copyContents函數(shù),將數(shù)組元素復(fù)制一份到新的緩沖區(qū)

找到_reserveCapacityAssumingUniqueBuffer的定義:

@inlinable
  @_semantics("array.mutate_unknown")
  internal mutating func _reserveCapacityAssumingUniqueBuffer(oldCount: Int) {

    let capacity = _buffer.capacity == 0

    _internalInvariant(capacity ||
                 _buffer.isMutableAndUniquelyReferenced())

    if _slowPath(oldCount + 1 > _buffer.capacity) {
      _createNewBuffer(bufferIsUnique: true,
                       minimumCapacity: oldCount + 1,
                       growForAppend: true)
    }
  }
  • 這個(gè)函數(shù)的本質(zhì)為了優(yōu)化性能
  • 當(dāng)count進(jìn)行+1后,超過(guò)數(shù)組容量,也會(huì)調(diào)用_createNewBuffer函數(shù)

找到_appendElementAssumeUniqueAndCapacity的定義:

  @inlinable
  @_semantics("array.mutate_unknown")
  internal mutating func _appendElementAssumeUniqueAndCapacity(
    _ oldCount: Int,
    newElement: __owned Element
  ) {
    _internalInvariant(_buffer.isMutableAndUniquelyReferenced())
    _internalInvariant(_buffer.capacity >= _buffer.count + 1)

    _buffer.count = oldCount + 1
    (_buffer.firstElementAddress + oldCount).initialize(to: newElement)
  }
  • oldCount進(jìn)行+1后,賦值給_buffer.count
  • 通過(guò)initialize(to: newElement)將新元素存儲(chǔ)到數(shù)組中

所以數(shù)組的擴(kuò)容,有兩種情況都會(huì)創(chuàng)建新的內(nèi)存空間:

  • 如果數(shù)組存在多個(gè)引用計(jì)數(shù),進(jìn)行寫時(shí)復(fù)制,創(chuàng)建新內(nèi)存空間存儲(chǔ)元素
  • 如果數(shù)組插入新元素后超過(guò)數(shù)組容量,同樣需要?jiǎng)?chuàng)建新內(nèi)存空間存儲(chǔ)元素

打開ArrayShared.swift文件,找到_growArrayCapacity的定義:

@_alwaysEmitIntoClient
internal func _growArrayCapacity(
  oldCapacity: Int, minimumCapacity: Int, growForAppend: Bool
) -> Int {
  if growForAppend {
    if oldCapacity < minimumCapacity {
      // When appending to an array, grow exponentially.
      return Swift.max(minimumCapacity, _growArrayCapacity(oldCapacity))
    }
    return oldCapacity
  }
  // If not for append, just use the specified capacity, ignoring oldCapacity.
  // This means that we "shrink" the buffer in case minimumCapacity is less
  // than oldCapacity.
  return minimumCapacity
}
  • 調(diào)用max()判斷并返回兩個(gè)參數(shù)中較大的值
  • 傳入的參數(shù)2,使用_growArrayCapacity(_ capacity:)函數(shù),傳入數(shù)組當(dāng)前的容量

找到_growArrayCapacity(_ capacity:)的定義:

@inlinable
internal func _growArrayCapacity(_ capacity: Int) -> Int {
  return capacity * 2
}
  • 數(shù)組每次擴(kuò)容,都會(huì)是之前容量的兩倍

通過(guò)LLDB調(diào)試來(lái)驗(yàn)證?下:

定義numbers數(shù)組,初始化1、2、3三個(gè)元素

var numbers = [1, 2, 3]

通過(guò)withUnsafePointer打印numbers內(nèi)存地址

通過(guò)x/8g查看numbers的內(nèi)存地址

通過(guò)x/8g查看內(nèi)存地址0x000000010070f000

  • 此時(shí)numberscount大小為3
  • capacity容量經(jīng)過(guò)&運(yùn)算顯示為6,但本質(zhì)上還是3

將元素4加入到numbers數(shù)組內(nèi),上面看到numbers的容量本質(zhì)上是3,想加入新元素,此時(shí)數(shù)組必須擴(kuò)容

numbers.append(4)

append元素后,再通過(guò)x/8g查看numbers的內(nèi)存地址

因?yàn)閿?shù)組的擴(kuò)容需要?jiǎng)?chuàng)建新的內(nèi)存空間存儲(chǔ)元素,所以打印出堆上的地址發(fā)生了改變:

  • append之前:打印地址0x000000010070f000
  • append之后:打印地址0x000000010060f3e0

通過(guò)x/8g查看內(nèi)存地址0x000000010060f3e0

  • 此時(shí)numberscount大小為4
  • 按上面源碼中的邏輯,數(shù)組每次擴(kuò)容,都會(huì)是之前容量的兩倍
  • 所以之前numbers容量為3,擴(kuò)容后應(yīng)該為6
  • 再經(jīng)過(guò)&運(yùn)算顯示為12,將其轉(zhuǎn)為16進(jìn)制0xC
Array的賦值

定義numbers數(shù)組,初始化1、23三個(gè)元素

var numbers = [1, 2, 3]

numbers賦值給tmp

var tmp = numbers

通過(guò)x/8g分別查看numberstmp堆上的地址,此時(shí)它們是一樣的

對(duì)tmp數(shù)組index=0的元素進(jìn)行修改

tmp[0] = 2

再通過(guò)x/8g分別查看numberstmp堆上的地址,此時(shí)tmp發(fā)生改變,原因是觸發(fā)了寫時(shí)復(fù)制

分別po打印numberstmp的值,可以看到numbers并沒(méi)有受到影響

分析SIL代碼
var numbers = [1, 2, 3]
var tmp = numbers
tmp[0] = 2

將上述代碼生成SIL文件:swiftc -emit-sil main.swift | xcrun swift-demangle

// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  alloc_global @main.numbers : [Swift.Int]           // id: %2
  %3 = global_addr @main.numbers : [Swift.Int] : $*Array<Int> // users: %23, %26
  %4 = integer_literal $Builtin.Word, 3           // user: %6
  // function_ref _allocateUninitializedArray<A>(_:)
  %5 = function_ref @Swift._allocateUninitializedArray<A>(Builtin.Word) -> ([A], Builtin.RawPointer) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %6
  %6 = apply %5<Int>(%4) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // users: %8, %7
  %7 = tuple_extract %6 : $(Array<Int>, Builtin.RawPointer), 0 // user: %23
  %8 = tuple_extract %6 : $(Array<Int>, Builtin.RawPointer), 1 // user: %9
  %9 = pointer_to_address %8 : $Builtin.RawPointer to [strict] $*Int // users: %12, %19, %14
  %10 = integer_literal $Builtin.Int64, 1         // user: %11
  %11 = struct $Int (%10 : $Builtin.Int64)        // user: %12
  store %11 to %9 : $*Int                         // id: %12
  %13 = integer_literal $Builtin.Word, 1          // user: %14
  %14 = index_addr %9 : $*Int, %13 : $Builtin.Word // user: %17
  %15 = integer_literal $Builtin.Int64, 2         // user: %16
  %16 = struct $Int (%15 : $Builtin.Int64)        // user: %17
  store %16 to %14 : $*Int                        // id: %17
  %18 = integer_literal $Builtin.Word, 2          // user: %19
  %19 = index_addr %9 : $*Int, %18 : $Builtin.Word // user: %22
  %20 = integer_literal $Builtin.Int64, 3         // user: %21
  %21 = struct $Int (%20 : $Builtin.Int64)        // user: %22
  store %21 to %19 : $*Int                        // id: %22
  store %7 to %3 : $*Array<Int>                   // id: %23
  alloc_global @main.tmp : [Swift.Int]               // id: %24
  %25 = global_addr @main.tmp : [Swift.Int] : $*Array<Int> // users: %33, %27
  %26 = begin_access [read] [dynamic] %3 : $*Array<Int> // users: %28, %27
  copy_addr %26 to [initialization] %25 : $*Array<Int> // id: %27
  end_access %26 : $*Array<Int>                   // id: %28
  %29 = integer_literal $Builtin.Int64, 0         // user: %30
  %30 = struct $Int (%29 : $Builtin.Int64)        // user: %35
  %31 = integer_literal $Builtin.Int64, 2         // user: %32
  %32 = struct $Int (%31 : $Builtin.Int64)        // user: %37
  %33 = begin_access [modify] [dynamic] %25 : $*Array<Int> // users: %39, %35
  // function_ref Array.subscript.modify
  %34 = function_ref @Swift.Array.subscript.modify : (Swift.Int) -> A : $@yield_once @convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> @yields @inout τ_0_0 // user: %35
  (%35, %36) = begin_apply %34<Int>(%30, %33) : $@yield_once @convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> @yields @inout τ_0_0 // users: %37, %38
  store %32 to %35 : $*Int                        // id: %37
  end_apply %36                                   // id: %38
  end_access %33 : $*Array<Int>                   // id: %39
  %40 = integer_literal $Builtin.Int32, 0         // user: %41
  %41 = struct $Int32 (%40 : $Builtin.Int32)      // user: %42
  return %41 : $Int32                             // id: %42
} // end sil function 'main'
  • 賦值操作:使用copy_addr,將numbers內(nèi)存里的值賦值給tmp
  • 修改tmp:修改過(guò)程中,調(diào)用了Arraysubscript屬性的modify函數(shù)
源碼分析

打開Array.swift文件,找到subscript的定義:

  @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
    }
  }
  • subscript函數(shù)內(nèi),分別有get函數(shù)和_modify函數(shù)
  • _modify函數(shù)內(nèi)調(diào)用_makeMutableAndUnique函數(shù)

找到_makeMutableAndUnique的定義:

  @inlinable
  @_semantics("array.make_mutable")
  internal mutating func _makeMutableAndUnique() {
    if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) {
      _createNewBuffer(bufferIsUnique: false, minimumCapacity: count,
                       growForAppend: false)
    }
  }
  • 判斷是否存在多個(gè)引用計(jì)數(shù)
  • 如果存在多個(gè),調(diào)用_createNewBuffer函數(shù)
  • _createNewBuffer函數(shù),重新開辟了內(nèi)存空間,此時(shí)修改tmp的值和numbers的值沒(méi)有任何關(guān)聯(lián)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容