美文网首页swift mvvmSwift那些事
Swift底层进阶--019:Array源码解析

Swift底层进阶--019:Array源码解析

作者: 帅驼驼 | 来源:发表于2021-02-07 15:53 被阅读0次
    • Array使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。
    • Array会强制检测元素的类型,如果类型不同则会报错。Swift数组应该遵循像Array<Element>这样的形式,其中Element是这个数组中唯一允许存在的数据类型。
    • 如果创建一个数组,并赋值给一个变量,则创建的集合就是可以修改的。这意味着在创建数组后,可以通过添加、删除、修改的方式改变数组里的元素。
    • 如果将一个数组赋值给常量,数组就不可更改,并且数组的大小和内容都不可以修改。
    Array的创建

    Array有很多创建⽅式:

    通过字面量方式创建一个Int类型数组

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

    通过字面量方式创建一个String类型数组

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

    也可以使用Array()初始化方法创建数组,但这种方式必须指明数据类型,否则编译报错

    使用Array()方法创建数组

    var emptyArray: [Int] = Array()
    

    这种写法,等同于上述方式

    var emptyArray: Array<Int> = Array()
    

    当访问数组的元素不存在时,运行时会报错:数组越界(Index out of range

    如果想在数组创建时给定初始值,可以使用Array(repeating:, count:)方法

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

    得到10个长度的数组,初始值全部为0

    分析SIL代码

    通过SIL代码分析数组的创建过程

    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:通过_allocateUninitializedArray函数进行初始化
    • %7:从元组中获取第0个元素,拿到一个Array
    • %8:从元组中获取第1个元素,拿到一个地址
    • %9:对%8的地址引用,*Int类型
    • %10、%11:创建字面量1
    • store %11 to %9:将字面量1存储到%9
    • %13:地址偏移
    • %14:给定引用值数组的地址,返回数组中index=1位置的地址
    • 下面以此类推,将剩余元素依次放入到连续的存储空间中
    源码分析

    Array应该是⼀个值类型,但SIL代码中却出现了_alloc函数的调用,通过源码分析,看一下它在底层到底做了什么

    打开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)
    }
    
    • 如果进入count > 0的条件分支
    • 使用_allocateUninitialized函数分配堆内存,先创建_ContiguousArrayStorage类,并在_ContiguousArrayStorage类的尾部,分配builtinCount元素大小的连续内存空间,存放Element.self
    • 调用Array_adoptStorage函数
    • 返回一个元组类型

    _allocateUninitialized函数内部调用了HeapObject.cpp文件中的swift_allocObject函数

    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)
      }
    
    • 通过_ContiguousArrayBuffer函数创建innerBuffer私有变量
    • 返回元组类型
    • Array(_buffer: _Buffer(_buffer: , shiftedToStartIndex: ))Array的实例对象
    • innerBuffer.firstElementAddress:当前元素的首地址

    找到_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也是一个结构体
    • 里面只有一个_storage变量

    找到init的定义:

      @inlinable
      internal init(count: Int, storage: _ContiguousArrayStorage<Element>) {
        _storage = storage
    
        _initStorageHeader(count: count, capacity: count)
      }
    
    • init方法中,_storage存储的是alloc出来的堆上的内存

    找到_storage的定义:

      @usableFromInline
      internal var _storage: __ContiguousArrayStorageBase
    
    • _storage是一个__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是一个Class类型
    • 有一个_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函数内调用_ArrayBody函数,赋值给_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是一个结构体
    • _ArrayBody结构体中,包含一个_SwiftArrayBodyStorage类型的_storage变量

    打开GlobalObjects.h文件,找到_SwiftArrayBodyStorage的定义:

    struct _SwiftArrayBodyStorage {
      __swift_intptr_t count;
      __swift_uintptr_t _capacityAndFlags;
    };
    
    • _SwiftArrayBodyStorage也是一个结构体
    • 包含count_capacityAndFlags两个成员属性
    Array内存布局

    现在梳理一下Array的内存布局:

    Struct Array->Struct _ContiguousArrayBuffer->Class __ContiguousArrayStorageBase-> 包含⼀个Struct ArrayBody属性 -> 包含⼀个struct _SwiftArrayBodyStorage属性 -> 包含count_capacityAndFlags两个属性

    这⾥⼤致推导出Array的内存布局,其实最主要和__ContiguousArrayStorageBase类有关,因为之前的都是值类型

    通过LLDB调试来验证⼀下:

    定义numbers数组,初始化123三个元素

    var numbers = [1, 2, 3]
    

    通过withUnsafePointer打印numbers内存地址

    通过x/8g查看numbers的内存地址,里面包含了一个堆上的地址0x0000000100410830

    通过x/8g查看内存地址0x0000000100410830

    • 0x00007fff995404d8:元类型metaData,通过cat address打印,它存储在__DATA.__bss段,即:未初始化段
    • 0x0000000200000002:引用计数refCount
    • 0x0000000000000003:数组大小count,这里输出为3
    • 0x0000000000000006:容量_capacityAndFlags,这里输出为6。这是什么情况?难道容量是数组大小的2倍吗?
    • 后面三位分别是数组内元素123

    通过po打印numbers,由于返回的是元素首地址,所以直接输出的是值,而不是Array的内存数据结构

    Array的容量(capacity)

    numbers数组的大小为3,为什么容量却是6,难道容量是数组大小的2倍

    打开ArrayBody.swift文件,在_ArrayBody结构体内,找到对_storage变量进行赋值的代码:

    _storage = _SwiftArrayBodyStorage(
          count: count,
          _capacityAndFlags:
            (UInt(truncatingIfNeeded: capacity) &<< 1) |
            (elementTypeIsBridgedVerbatim ? 1 : 0))
    
    • _capacityAndFlags容量被赋值时,对capacity进行了&运算的操作
    • elementTypeIsBridgedVerbatim用来判断当前元素是否允许桥接ObjC

    在上面的案例中,numbers数组的容量应该为3,经过&运算操作输出为6

    • 本质上Array的容量还是3
    Array不同创建⽅式的差别

    Array不同的创建⽅式,导致Metadata有所差别

    使用字面量方式或者通过Array(repeating:, count:)方法创建数组

    • Metadata打印为InitialAllocationPool,存储在__DATA.__bss

    使用Array()方法创建数组

    • Metadata打印的类型是_swiftEmptyArrayStorage,存储在__DATA.__data

    打开GlobalObjects.h文件,找到_SwiftEmptyArrayStorage的定义:

    struct _SwiftEmptyArrayStorage {
      struct HeapObject header;
      struct _SwiftArrayBodyStorage body;
    };
    
    • _SwiftEmptyArrayStorage是一个结构体,里面包含HeapObject结构体和_SwiftArrayBodyStorage结构体
    Array的数据的拼接

    Array进行append操作时,肯定会涉及到数组的扩容

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

    对于数组的扩容,底层是如何处理的?

    打开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:判断当前Array是否有唯一的引用计数
    • _reserveCapacityAssumingUniqueBuffer:将数组进行反转
    • _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:判断是否存在多个引用计数
    • 如果存在多个,调用_createNewBuffer函数触发写时复制

    找到_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:获取到数组扩容后的容量
    • 如果bufferIsUniquetrue,表示引用计数唯一,调用moveInitialize函数,将数组元素移动到新的缓冲区,而不是复制
    • 如果为false,表示存在多个引用计数,调用_copyContents函数,将数组元素复制一份到新的缓冲区

    找到_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)
        }
      }
    
    • 这个函数的本质为了优化性能
    • count进行+1后,超过数组容量,也会调用_createNewBuffer函数

    找到_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进行+1后,赋值给_buffer.count
    • 通过initialize(to: newElement)将新元素存储到数组中

    所以数组的扩容,有两种情况都会创建新的内存空间:

    • 如果数组存在多个引用计数,进行写时复制,创建新内存空间存储元素
    • 如果数组插入新元素后超过数组容量,同样需要创建新内存空间存储元素

    打开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
    }
    
    • 调用max()判断并返回两个参数中较大的值
    • 传入的参数2,使用_growArrayCapacity(_ capacity:)函数,传入数组当前的容量

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

    @inlinable
    internal func _growArrayCapacity(_ capacity: Int) -> Int {
      return capacity * 2
    }
    
    • 数组每次扩容,都会是之前容量的两倍

    通过LLDB调试来验证⼀下:

    定义numbers数组,初始化123三个元素

    var numbers = [1, 2, 3]
    

    通过withUnsafePointer打印numbers内存地址

    通过x/8g查看numbers的内存地址

    通过x/8g查看内存地址0x000000010070f000

    • 此时numberscount大小为3
    • capacity容量经过&运算显示为6,但本质上还是3

    将元素4加入到numbers数组内,上面看到numbers的容量本质上是3,想加入新元素,此时数组必须扩容

    numbers.append(4)
    

    append元素后,再通过x/8g查看numbers的内存地址

    因为数组的扩容需要创建新的内存空间存储元素,所以打印出堆上的地址发生了改变:

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

    通过x/8g查看内存地址0x000000010060f3e0

    • 此时numberscount大小为4
    • 按上面源码中的逻辑,数组每次扩容,都会是之前容量的两倍
    • 所以之前numbers容量为3,扩容后应该为6
    • 再经过&运算显示为12,将其转为16进制0xC
    Array的赋值

    定义numbers数组,初始化123三个元素

    var numbers = [1, 2, 3]
    

    numbers赋值给tmp

    var tmp = numbers
    

    通过x/8g分别查看numberstmp堆上的地址,此时它们是一样的

    tmp数组index=0的元素进行修改

    tmp[0] = 2
    

    再通过x/8g分别查看numberstmp堆上的地址,此时tmp发生改变,原因是触发了写时复制

    分别po打印numberstmp的值,可以看到numbers并没有受到影响

    分析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内存里的值赋值给tmp
    • 修改tmp:修改过程中,调用了Arraysubscript属性的modify函数
    源码分析

    打开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函数内,分别有get函数和_modify函数
    • _modify函数内调用_makeMutableAndUnique函数

    找到_makeMutableAndUnique的定义:

      @inlinable
      @_semantics("array.make_mutable")
      internal mutating func _makeMutableAndUnique() {
        if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) {
          _createNewBuffer(bufferIsUnique: false, minimumCapacity: count,
                           growForAppend: false)
        }
      }
    
    • 判断是否存在多个引用计数
    • 如果存在多个,调用_createNewBuffer函数
    • _createNewBuffer函数,重新开辟了内存空间,此时修改tmp的值和numbers的值没有任何关联

    相关文章

      网友评论

        本文标题:Swift底层进阶--019:Array源码解析

        本文链接:https://www.haomeiwen.com/subject/ylswtltx.html