日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Swift标准库源码阅读笔记 - Array和ContiguousArray

發(fā)布時(shí)間:2025/5/22 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Swift标准库源码阅读笔记 - Array和ContiguousArray 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

關(guān)于 ContiguousArray ,這邊有喵神的文章介紹的很詳細(xì)了,可以先看看這個(gè)文章。

Array

接著喵神的思路,看一下 Array 以下是從源碼中截取的代碼片段。

public struct Array<Element>: _DestructorSafeContainer {#if _runtime(_ObjC)internal typealias _Buffer = _ArrayBuffer<Element>#elseinternal typealias _Buffer = _ContiguousArrayBuffer<Element>#endifinternal var _buffer: _Bufferinternal init(_buffer: _Buffer) {self._buffer = _buffer} } 復(fù)制代碼

if _runtime(_ObjC) 等價(jià)于 #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS),從這個(gè)操作也可以看出 Swift 的野心不僅僅只是替換 Objective-C那么簡(jiǎn)單,而是往更加寬泛的方向發(fā)展。由于本次主要是研究在 iOS下的開發(fā),所以主要看一下 _ArrayBuffer。

_ArrayBuffer

去掉了注釋和與類型檢查相關(guān)的屬性和方法。

internal typealias _ArrayBridgeStorage= _BridgeStorage<_ContiguousArrayStorageBase, _NSArrayCore>internal struct _ArrayBuffer<Element> : _ArrayBufferProtocol {internal init() {_storage = _ArrayBridgeStorage(native: _emptyArrayStorage)}internal var _storage: _ArrayBridgeStorage } 復(fù)制代碼

可見 _ArrayBuffer 僅有一個(gè)存儲(chǔ)屬性 _storage ,它的類型 _ArrayBridgeStorage,本質(zhì)上是 _BridgeStorage。
_NSArrayCore 其實(shí)是一個(gè)協(xié)議,定義了一些 NSArray 的方法,主要是為了橋接 Objective-C 的 NSArray。
最主要的初始化函數(shù),是通過 _emptyArrayStorage 來初始化 _storage。

實(shí)際上 _emptyArrayStorage 是 _EmptyArrayStorage 的實(shí)例,主要作用是初始化一個(gè)空的數(shù)組,并且將內(nèi)存指定在堆上。

internal var _emptyArrayStorage : _EmptyArrayStorage {return Builtin.bridgeFromRawPointer(Builtin.addressof(&_swiftEmptyArrayStorage)) } 復(fù)制代碼

_BridgeStorage

struct _BridgeStorage<NativeClass: AnyObject, ObjCClass: AnyObject> {typealias Native = NativeClasstypealias ObjC = ObjCClassinit(native: Native, bits: Int) {rawValue = _makeNativeBridgeObject(native, UInt(bits) << _objectPointerLowSpareBitShift)}init(objC: ObjC) {rawValue = _makeObjCBridgeObject(objC)}init(native: Native) {rawValue = Builtin.reinterpretCast(native)}internal var rawValue: Builtin.BridgeObject 復(fù)制代碼

_BridgeStorage 實(shí)際上區(qū)分 是否是 class、@objc ,進(jìn)而提供不同的存儲(chǔ)策略,為上層調(diào)用提供了不同的接口,以及類型判斷,通過 Builtin.BridgeObject 這個(gè)中間參數(shù),實(shí)現(xiàn)不同的儲(chǔ)存策略。

The ContiguousArray type is a specialized array that always stores its elements in a contiguous region of memory. This contrasts with Array, which can store its elements in either a contiguous region of memory or an NSArray instance if its Element type is a class or @objc protocol.

If your array’s Element type is a class or @objc protocol and you do not need to bridge the array to NSArray or pass the array to Objective-C APIs, using ContiguousArray may be more efficient and have more predictable performance than Array. If the array’s Element type is a struct or enumeration, Array and ContiguousArray should have similar efficiency.

正因?yàn)閮?chǔ)存策略的不同,特別是在class 或者 @objc,如果不考慮橋接到 NSArray 或者調(diào)用 Objective-C,蘋果建議我們使用 ContiguousArray,會(huì)更有效率。

Array 和 ContiguousArray 區(qū)別

通過一些常用的數(shù)組操作,來看看兩者之間的區(qū)別。

append

ContiguousArray

public mutating func append(_ newElement: Element) {_makeUniqueAndReserveCapacityIfNotUnique()let oldCount = _getCount()_reserveCapacityAssumingUniqueBuffer(oldCount: oldCount)_appendElementAssumeUniqueAndCapacity(oldCount, newElement: newElement)}internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() {if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) {_copyToNewBuffer(oldCount: _buffer.count)}}internal mutating func _reserveCapacityAssumingUniqueBuffer(oldCount: Int) {let capacity = _buffer.capacity == 0if _slowPath(oldCount + 1 > _buffer.capacity) {_copyToNewBuffer(oldCount: oldCount)}}internal mutating func _copyToNewBuffer(oldCount: Int) {let newCount = oldCount + 1var newBuffer = _buffer._forceCreateUniqueMutableBuffer(countForNewBuffer: oldCount, minNewCapacity: newCount)_buffer._arrayOutOfPlaceUpdate(&newBuffer, oldCount, 0, _IgnorePointer())}internal mutating func _appendElementAssumeUniqueAndCapacity(_ oldCount: Int,newElement: Element) {_buffer.count = oldCount + 1(_buffer.firstElementAddress + oldCount).initialize(to: newElement)} 復(fù)制代碼

_makeUniqueAndReserveCapacityIfNotUnique() 檢查數(shù)組是否是唯一持有者,以及是否是可變數(shù)組。
_reserveCapacityAssumingUniqueBuffer(oldCount: oldCount)檢查數(shù)組內(nèi)的元素個(gè)數(shù)加一后,是否超出超過所分配的空間。
前兩個(gè)方法在檢查之后都調(diào)用了 _copyToNewBuffer ,主要操作是如果當(dāng)前數(shù)組需要申請(qǐng)空間,則申請(qǐng)空間,然后再?gòu)?fù)制 buffer 。
_appendElementAssumeUniqueAndCapacity(oldCount, newElement: newElement) 從首地址后的第 oldCount 個(gè)存儲(chǔ)空間內(nèi),初始化 newElement 。

Array

Array 實(shí)現(xiàn)的過程與 ContiguousArray 差不多,但是還是有一些區(qū)別,具體看看,主要的區(qū)別存在于_ContiguousArrayBuffer 和 _ArrayBuffer

_ContiguousArrayBuffer

internal var firstElementAddress: UnsafeMutablePointer<Element> {return UnsafeMutablePointer(Builtin.projectTailElems(_storage,Element.self)) } 復(fù)制代碼

直接返回了內(nèi)存地址。

_ArrayBuffer

internal var firstElementAddress: UnsafeMutablePointer<Element> {_sanityCheck(_isNative, "must be a native buffer")return _native.firstElementAddress}internal var _native: NativeBuffer {return NativeBuffer(_isClassOrObjCExistential(Element.self)? _storage.nativeInstance : _storage.nativeInstance_noSpareBits)}internal typealias NativeBuffer = _ContiguousArrayBuffer<Element> 復(fù)制代碼

從調(diào)用的情況來看,本質(zhì)上還是調(diào)用了 _ContiguousArrayBuffer的firstElementAddress

但是在創(chuàng)建時(shí),會(huì)有類型檢查。

_isClassOrObjCExistential(Element.self)檢查是否是類或者@objc修飾的。

在上述中檢查持有者是否唯一和數(shù)組是否可變的函數(shù)中, 其實(shí)是調(diào)用了 _buffer內(nèi)部的 isMutableAndUniquelyReferenced()。

_ContiguousArrayBuffer

@inlinableinternal mutating func isUniquelyReferenced() -> Bool {return _isUnique(&_storage)} 復(fù)制代碼internal func _isUnique<T>(_ object: inout T) -> Bool {return Bool(Builtin.isUnique(&object)) } 復(fù)制代碼

最后調(diào)用的 Builtin 中的 isUnique。

_ArrayBuffer

internal mutating func isUniquelyReferenced() -> Bool {if !_isClassOrObjCExistential(Element.self) {return _storage.isUniquelyReferenced_native_noSpareBits()}if !_storage.isUniquelyReferencedNative() {return false}return _isNative}mutating func isUniquelyReferencedNative() -> Bool {return _isUnique(&rawValue)}mutating func isUniquelyReferenced_native_noSpareBits() -> Bool {_sanityCheck(isNative)return _isUnique_native(&rawValue)}func _isUnique_native<T>(_ object: inout T) -> Bool {_sanityCheck((_bitPattern(Builtin.reinterpretCast(object)) & _objectPointerSpareBits)== 0)_sanityCheck(_usesNativeSwiftReferenceCounting(type(of: Builtin.reinterpretCast(object) as AnyObject)))return Bool(Builtin.isUnique_native(&object)) } 復(fù)制代碼

如果是 class 或者 @objc 和 _ContiguousBuffer 一樣。如果不是則需要調(diào)用 Builtin 中的 _isUnique_native,即要檢查是否唯一,還要檢查是否是 Swift 原生 而不是 NSArray。 相對(duì)于 _ContiguousArrayBuffer 由于 _ArrayBuffer 承載了需要橋接到 NSArray 的功能,所以多了一些類型檢查的操作。

insert

ContiguousArray

//ContiguousArraypublic mutating func insert(_ newElement: Element, at i: Int) {_checkIndex(i)self.replaceSubrange(i..<i, with: CollectionOfOne(newElement))}public mutating func replaceSubrange<C>(_ subrange: Range<Int>,with newElements: C) where C : Collection, C.Element == Element {let oldCount = _buffer.countlet eraseCount = subrange.countlet insertCount = newElements.countlet growth = insertCount - eraseCountif _buffer.requestUniqueMutableBackingBuffer(minimumCapacity: oldCount + growth) != nil {_buffer.replaceSubrange(subrange, with: insertCount, elementsOf: newElements)} else {_buffer._arrayOutOfPlaceReplace(subrange, with: newElements, count: insertCount)}}internal mutating func requestUniqueMutableBackingBuffer(minimumCapacity: Int) -> _ContiguousArrayBuffer<Element>? {if _fastPath(isUniquelyReferenced() && capacity >= minimumCapacity) {return self}return nil}//extension ArrayProtocolinternal mutating func replaceSubrange<C>(_ subrange: Range<Int>,with newCount: Int,elementsOf newValues: C) where C : Collection, C.Element == Element {_sanityCheck(startIndex == 0, "_SliceBuffer should override this function.")let oldCount = self.count //現(xiàn)有數(shù)組大小let eraseCount = subrange.count //需要替換大小let growth = newCount - eraseCount //目標(biāo)大小 和 需要替換大小 的差值self.count = oldCount + growth //替換后的數(shù)組大小let elements = self.subscriptBaseAddress //數(shù)組首地址。let oldTailIndex = subrange.upperBound let oldTailStart = elements + oldTailIndex //需要替換的尾地址。let newTailIndex = oldTailIndex + growth //需要增加的空間的尾下標(biāo)let newTailStart = oldTailStart + growth //需要增加的空間的尾地址let tailCount = oldCount - subrange.upperBound //需要移動(dòng)的內(nèi)存空間大小if growth > 0 {var i = newValues.startIndexfor j in subrange {elements[j] = newValues[i]newValues.formIndex(after: &i)}for j in oldTailIndex..<newTailIndex {(elements + j).initialize(to: newValues[i])newValues.formIndex(after: &i)}_expectEnd(of: newValues, is: i)}else { var i = subrange.lowerBoundvar j = newValues.startIndexfor _ in 0..<newCount {elements[i] = newValues[j]i += 1newValues.formIndex(after: &j)}_expectEnd(of: newValues, is: j)if growth == 0 {return}let shrinkage = -growthif tailCount > shrinkage { newTailStart.moveAssign(from: oldTailStart, count: shrinkage)oldTailStart.moveInitialize(from: oldTailStart + shrinkage, count: tailCount - shrinkage)}else { newTailStart.moveAssign(from: oldTailStart, count: tailCount)(newTailStart + tailCount).deinitialize(count: shrinkage - tailCount)}}}復(fù)制代碼

insert 內(nèi)部實(shí)際是 調(diào)用了 replaceSubrange。 而在 replaceSubrange 的操作是,判斷內(nèi)存空間是否夠用,和持有者是否唯一,如果有一個(gè)不滿足條件則復(fù)制 buffer 到新的內(nèi)存空間,并且根據(jù)需求分配好內(nèi)存空間大小。

而 _buffer 內(nèi)部的 replaceSubrange:

  • 計(jì)算 growth 值看所替換的大小和目標(biāo)大小差值是多少。
  • 如果 growth > 0 ,則需要將現(xiàn)有的內(nèi)存空間向后移動(dòng) growth 位。
  • 替換所需要替換的值。
  • 超出的部分重新分配內(nèi)存并初始化值。
  • 如果 growth <= 0,則將現(xiàn)有的值替換成新的值即可。
  • 如果 growth < 0,則將不需要的內(nèi)存空間回收即可。(ps:刪除多個(gè)元素或者需要替換的大小大于目標(biāo)大小)。

Array insert 兩者基本一致,唯一的區(qū)別和 append 一樣在 在buffer的內(nèi)部方法,isUniquelyReferenced() 中,多了一些類型檢查。

remove

ContiguousArray

public mutating func remove(at index: Int) -> Element {_makeUniqueAndReserveCapacityIfNotUnique()let newCount = _getCount() - 1let pointer = (_buffer.firstElementAddress + index)let result = pointer.move()pointer.moveInitialize(from: pointer + 1, count: newCount - index)_buffer.count = newCountreturn result} 復(fù)制代碼

檢查數(shù)組持有者是否唯一,取出所要?jiǎng)h除的內(nèi)存地址,通過將當(dāng)前的內(nèi)存區(qū)域覆蓋為一個(gè)未初始化的內(nèi)存空間,以達(dá)到回收內(nèi)存空間的作用,進(jìn)而達(dá)到刪除數(shù)組元素的作用。

Array

與 ContiguousArray 的區(qū)別就在于 _makeUniqueAndReserveCapacityIfNotUnique() 前面已經(jīng)提到過,仍然是多了一些類型檢查。

subscript

ContiguousArray

//ContiguousArray public subscript(index: Int) -> Element {get {let wasNativeTypeChecked = _hoistableIsNativeTypeChecked()let token = _checkSubscript(index, wasNativeTypeChecked: wasNativeTypeChecked)return _getElement(index, wasNativeTypeChecked: wasNativeTypeChecked,matchingSubscriptCheck: token)}}publicfunc _getElement(_ index: Int,wasNativeTypeChecked : Bool,matchingSubscriptCheck: _DependenceToken) -> Element {#if falsereturn _buffer.getElement(index, wasNativeTypeChecked: wasNativeTypeChecked)#elsereturn _buffer.getElement(index)#endif}//ContiguousArrayBufferinternal func getElement(_ i: Int) -> Element {return firstElementAddress[i]} 復(fù)制代碼

_hoistableIsNativeTypeChecked() 不做任何檢查,直接返回 true。 _checkSubscript(index, wasNativeTypeChecked: wasNativeTypeChecked) 檢查 index 是否越界。 _getElement 最終還是操作內(nèi)存,通過 firstElementAddress 偏移量取出值。

Array

//Arraypublicfunc _checkSubscript(_ index: Int, wasNativeTypeChecked: Bool) -> _DependenceToken { #if _runtime(_ObjC)_buffer._checkInoutAndNativeTypeCheckedBounds(index, wasNativeTypeChecked: wasNativeTypeChecked) #else_buffer._checkValidSubscript(index) #endifreturn _DependenceToken()}func _hoistableIsNativeTypeChecked() -> Bool {return _buffer.arrayPropertyIsNativeTypeChecked}//ArrayBufferinternal var arrayPropertyIsNativeTypeChecked: Bool {return _hasNativeBuffer}internal var _isNativeTypeChecked: Bool {if !_isClassOrObjCExistential(Element.self) {return true} else {return _storage.isNativeWithClearedSpareBits(deferredTypeCheckMask)}} 復(fù)制代碼

在 ContiguousArray 中 _hoistableIsNativeTypeChecked() 直接返回 true, 而 Array 中如果不是 class 或者 @objc 會(huì)返回 ture,否則會(huì)檢查是否可以橋接到 Swift。

而在 Array 中 _checkSubscript 調(diào)用的 _buffer 內(nèi)部函數(shù)也不一樣,下面來具體看一看內(nèi)部實(shí)現(xiàn)。

//ArrayBufferinternal func _checkInoutAndNativeTypeCheckedBounds(_ index: Int, wasNativeTypeChecked: Bool) {_precondition(_isNativeTypeChecked == wasNativeTypeChecked,"inout rules were violated: the array was overwritten")if _fastPath(wasNativeTypeChecked) {_native._checkValidSubscript(index)}}//ContiguousArrayBufferinternal func _checkValidSubscript(_ index : Int) {_precondition((index >= 0) && (index < count),"Index out of range")} 復(fù)制代碼

本質(zhì)上就是多了一些是否是類型檢查。

//Array func _getElement(_ index: Int,wasNativeTypeChecked : Bool,matchingSubscriptCheck: _DependenceToken) -> Element { #if _runtime(_ObjC)return _buffer.getElement(index, wasNativeTypeChecked: wasNativeTypeChecked) #elsereturn _buffer.getElement(index) #endif}//ArrayBuffer internal func getElement(_ i: Int, wasNativeTypeChecked: Bool) -> Element {if _fastPath(wasNativeTypeChecked) {return _nativeTypeChecked[i]}return unsafeBitCast(_getElementSlowPath(i), to: Element.self)}internal func _getElementSlowPath(_ i: Int) -> AnyObject {let element: AnyObjectif _isNative {_native._checkValidSubscript(i)element = cast(toBufferOf: AnyObject.self)._native[i]} else {element = _nonNative.objectAt(i)}return element}//ContiguousArrayBufferinternal subscript(i: Int) -> Element {get {return getElement(i)}} 復(fù)制代碼

在 _buffer 內(nèi)部的 getElement , 與 ContiguousArray 不同的是需要適配橋接到 NSArray 的情況,如果是 非NSArray 的情況調(diào)用的是 ContiguousArrayBuffer 內(nèi)部的 subscript ,和 ContiguousArray 相同。

總結(jié)

從增刪改查來看,不管是 ContiguousArray 還是 Array 最終都是操作內(nèi)存,稍顯區(qū)別的就是 Array 需要更多的類型檢查。所以當(dāng)不需要 Objective-C,還是盡量使用 ContiguousArray 。 下面是對(duì)數(shù)組中一些批量操作的總結(jié):

  • removeAll 、insert<C>(contentsOf: C, at: Int) 、 removeSubrange:最終調(diào)用的是 replaceSubrange
  • append<S : Sequence>(contentsOf newElements: S) 和 init(repeating repeatedValue: Element, count: Int):最終都是操作內(nèi)存,循環(huán)初始化新的內(nèi)存空間和值。

有什么不正確的地方,歡迎指出。

總結(jié)

以上是生活随笔為你收集整理的Swift标准库源码阅读笔记 - Array和ContiguousArray的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。