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
函数,赋值给_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
是一个结构体_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
数组,初始化1
、2
、3
三个元素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倍
吗?- 后面三位分别是数组内元素
1
、2
、3
通过
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
:将数组进行反转_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
:判断是否存在多个引用计数- 如果存在多个,调用
_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
:获取到数组扩容后的容量- 如果
bufferIsUnique
为true
,表示引用计数唯一,调用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
数组,初始化1
、2
、3
三个元素var numbers = [1, 2, 3]
通过
withUnsafePointer
打印numbers
内存地址
通过
x/8g
查看numbers
的内存地址
通过
x/8g
查看内存地址0x000000010070f000
- 此时
numbers
的count
大小为3
capacity
容量经过&
运算显示为6
,但本质上还是3
将元素
4
加入到numbers
数组内,上面看到numbers
的容量本质上是3
,想加入新元素,此时数组必须扩容numbers.append(4)
append
元素后,再通过x/8g
查看numbers
的内存地址
因为数组的扩容需要创建新的内存空间存储元素,所以打印出堆上的地址发生了改变:
append
之前:打印地址0x000000010070f000
append
之后:打印地址0x000000010060f3e0
通过
x/8g
查看内存地址0x000000010060f3e0
- 此时
numbers
的count
大小为4
- 按上面源码中的逻辑,数组每次扩容,都会是之前容量的两倍
- 所以之前
numbers
容量为3
,扩容后应该为6
- 再经过
&
运算显示为12
,将其转为16进制
是0xC
Array的赋值
定义
numbers
数组,初始化1
、2
、3
三个元素var numbers = [1, 2, 3]
将
numbers
赋值给tmp
var tmp = numbers
通过
x/8g
分别查看numbers
和tmp
堆上的地址,此时它们是一样的
对
tmp
数组index=0
的元素进行修改tmp[0] = 2
再通过
x/8g
分别查看numbers
和tmp
堆上的地址,此时tmp
发生改变,原因是触发了写时复制
分别
po
打印numbers
和tmp
的值,可以看到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
:修改过程中,调用了Array
的subscript
属性的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
的值没有任何关联