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)建字面量1store %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ù),賦值給_storage的countAndCapacity屬性
打開
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ù)refCount0x0000000000000003:數(shù)組大小count,這里輸出為30x0000000000000006:容量_capacityAndFlags,這里輸出為6。這是什么情況?難道容量是數(shù)組大小的2倍嗎?- 后面三位分別是數(shù)組內(nèi)元素
1、2、3通過(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)_appendElementAssumeUniqueAndCapacity:append元素到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ò)容后的容量- 如果
bufferIsUnique為true,表示引用計(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í)
numbers的count大小為3capacity容量經(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之前:打印地址0x000000010070f000append之后:打印地址0x000000010060f3e0通過(guò)
x/8g查看內(nèi)存地址0x000000010060f3e0
- 此時(shí)
numbers的count大小為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、2、3三個(gè)元素var numbers = [1, 2, 3]將
numbers賦值給tmpvar tmp = numbers通過(guò)
x/8g分別查看numbers和tmp堆上的地址,此時(shí)它們是一樣的
對(duì)
tmp數(shù)組index=0的元素進(jìn)行修改tmp[0] = 2再通過(guò)
x/8g分別查看numbers和tmp堆上的地址,此時(shí)tmp發(fā)生改變,原因是觸發(fā)了寫時(shí)復(fù)制
分別
po打印numbers和tmp的值,可以看到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)用了Array的subscript屬性的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)



















