LazySequence
先看一下下面代碼

從打印結(jié)果我們可以看到,首先Lazy的方式其實(shí)就是保存當(dāng)前集合和對(duì)應(yīng)的操作,然后在訪(fǎng)問(wèn)具體元素的時(shí)候,執(zhí)行對(duì)應(yīng)的操作。
LazyMapSequence<Array<Int>, Int>(_base: [1, 2, 3, 4], _transform: (Function))這是個(gè)什么東東呢?很明顯這是一個(gè)初始化的結(jié)構(gòu)體我們看看源碼是不是這個(gè)東西呢?

<Base: Sequence, Element> 這是類(lèi)型約束,這里不太明白的童鞋可以看我Swift-12:泛型講的泛型以及類(lèi)型約束,初始化中保存當(dāng)前集合_base和對(duì)應(yīng)的操作_transform
當(dāng)我們?cè)L問(wèn)元素的時(shí)候,本質(zhì)上,繼承了Sequence協(xié)議,通過(guò)訪(fǎng)問(wèn)自身迭代器_base.next()迭代當(dāng)前的元素,然后對(duì)單個(gè)元素進(jìn)行map(_transform)操作,就得來(lái)了map之后的元素

應(yīng)用:一般我們數(shù)組數(shù)據(jù)特別大的時(shí)候我們使用lazy關(guān)鍵字。
比如Array(0...10000)
Array
源碼比較多,我摘要一下Array的內(nèi)存布局
Struct Array------> Struct _ContiguousArrayBuffer----->Class __ContiguousArrayStorageBase------>包含了?個(gè)屬性 Struct ArrayBody ------ > 包含了?個(gè)屬性 struct _SwiftArrayBodyStorage -------> 包含了兩個(gè)屬性 count & _capacityAndFlags
類(lèi)里面存的分別是 metaData、refCount、count、 _capacityAndFlags、firstElementAddress
結(jié)構(gòu)體包含結(jié)構(gòu)體,結(jié)構(gòu)體中包含類(lèi),類(lèi)中在包含結(jié)構(gòu)體成員
我們來(lái)調(diào)試驗(yàn)證一下

append


如果是多個(gè)引用計(jì)數(shù),就會(huì)createNewBuffer,這也就是所謂的寫(xiě)時(shí)復(fù)制
這里補(bǔ)充一下:當(dāng)值類(lèi)型包含引用類(lèi)型時(shí),值類(lèi)型copy也會(huì)對(duì)內(nèi)部的引用類(lèi)型進(jìn)行+1,驗(yàn)證一下:

注意自定義的結(jié)構(gòu)體是沒(méi)有寫(xiě)時(shí)賦值功能的,這是集合等類(lèi)型swift底層實(shí)現(xiàn)的,我們接下來(lái)看看是如何實(shí)現(xiàn)的


-
判斷引用計(jì)數(shù),>1 重新分配內(nèi)存空間返回newBuffer,為了寫(xiě)時(shí)復(fù)制
2.如果加一之后>buffer的容量capacity也會(huì)從新開(kāi)辟內(nèi)存空間,將原有的element拼接到新的內(nèi)存空間中
image.png
image.png
斷點(diǎn)驗(yàn)證
ar numbers = [1,2,3,4,5,6]
numbers.append(7)
print("end")

寫(xiě)時(shí)復(fù)制總結(jié)
看了大部分網(wǎng)上關(guān)于寫(xiě)時(shí)復(fù)制的說(shuō)法,其實(shí)不夠準(zhǔn)確。
Array,Dictionary 和 Set 這樣的集合類(lèi)型是通過(guò)一種叫做寫(xiě)時(shí)復(fù)制 (copy-on-write) 的技術(shù)實(shí)現(xiàn)的,在內(nèi)部,這些 Array 結(jié)構(gòu)體含有指向某個(gè)內(nèi)存的引用,也就是前面的__ContiguousArrayStorageBase引用類(lèi)型的地址,這地址指向的內(nèi)存就是數(shù)組中元素所存儲(chǔ)的位置。賦值時(shí),兩個(gè)數(shù)組的引用指向的是內(nèi)存中同一個(gè)位置,這兩個(gè)數(shù)組共享了它們的存儲(chǔ)部分。不過(guò),當(dāng)我 們改變 其中一個(gè)數(shù)組的時(shí)候,這個(gè)共享會(huì)被檢測(cè)到,內(nèi)存將會(huì)被復(fù)制。這樣一來(lái),我們得以獨(dú)立地改變兩個(gè) 變量。昂貴的元素復(fù)制操作只在必要的時(shí)候發(fā)生,也就是我們改變這兩個(gè)變量的時(shí)候發(fā)生復(fù)制
這種行為就被稱(chēng)為寫(xiě)時(shí)復(fù)制。它的工作方式是,每當(dāng)數(shù)組被改變,它首先檢查它對(duì)存儲(chǔ)緩沖區(qū) 的引用是否是唯一的,或者說(shuō),檢查數(shù)組本身是不是這塊緩沖區(qū)的唯一擁有者。如果是,那么 緩沖區(qū)可以進(jìn)行原地變更;也不會(huì)有復(fù)制被進(jìn)行。不過(guò),如果緩沖區(qū)有一個(gè)以上的持有者 (如本 例中),那么數(shù)組就需要先進(jìn)行復(fù)制,然后對(duì)復(fù)制的值進(jìn)行變化,而保持其他的持有者不受影響。
Dictionary
我們知道字典是通過(guò)hash表的,那么什么是哈希表呢?
哈希表:
哈希表(hash table 也叫散列表),根據(jù)關(guān)鍵字(key value)直接訪(fǎng)問(wèn)在內(nèi)存中存儲(chǔ)位置的數(shù)據(jù)結(jié)構(gòu),利用數(shù)組可以直接通過(guò)下標(biāo)訪(fǎng)問(wèn)數(shù)據(jù)的特性,通過(guò)哈希函數(shù)找到下標(biāo),返回值,存放記錄的數(shù)組稱(chēng)作散列表
哈希函數(shù)
設(shè)計(jì)一個(gè)哈希函數(shù)主要有下面幾種方法
- 直接尋址法 hash(key) = a*k + b 之類(lèi)的線(xiàn)性函數(shù)
- 數(shù)字分析法 取關(guān)鍵字的若干位作為哈希地址
- 平方取中法 平方去中間的幾位作為哈希地址
- 折疊法 將key分割成位數(shù)相同的幾個(gè)部分 去這幾個(gè)部分的疊加和作為哈希地址
- 隨機(jī)數(shù)法
- 除留余數(shù)法 k%x 除在iOS中一般取2^n ,當(dāng)然x需要是2的倍數(shù),然后>> n ,k &(x-1) 與運(yùn)算替換模運(yùn)算提升效率
哈希沖突
如何解決哈希沖突
- 開(kāi)放尋址法
當(dāng)存儲(chǔ)數(shù)據(jù)的時(shí)候,當(dāng)前下標(biāo)有數(shù)據(jù),我們進(jìn)行存儲(chǔ)下標(biāo)+1或-1的位置,依次類(lèi)推 - 拉鏈法
即通過(guò)鏈表的形式也就是哈希拉鏈表,如果當(dāng)前下標(biāo)中有數(shù)據(jù),遍歷當(dāng)前鏈表,插到尾結(jié)點(diǎn)。
負(fù)載因子
填?表中的元素個(gè)數(shù) / 散列表的?度
一般超過(guò)3/4也就是0.75的時(shí)候就要對(duì)散列表擴(kuò)容。
回到Dictionary中,以字面量創(chuàng)建的字典源碼
public init(dictionaryLiteral elements: (Key, Value)...) {
//初始化一個(gè)結(jié)構(gòu)體
let native = _NativeDictionary<Key, Value>(capacity: elements.count)
for (key, value) in elements {
//判斷key是否存在
let (bucket, found) = native.find(key)
//若找到了key,則報(bào)錯(cuò),也就是創(chuàng)建時(shí)不能有相同的key否則崩潰
_precondition(!found, "Dictionary literal contains duplicate keys")
//插入
native._insert(at: bucket, key: key, value: value)
}
self.init(_native: native)
}
}
_NativeDictionary是什么?
internal struct _NativeDictionary<Key: Hashable, Value> {
@usableFromInline
internal typealias Element = (key: Key, value: Value)
/// See this comments on __RawDictionaryStorage and its subclasses to
/// understand why we store an untyped storage here.
@usableFromInline
internal var _storage: __RawDictionaryStorage
/// Constructs an instance from the empty singleton.
@inlinable
internal init() {
self._storage = __RawDictionaryStorage.empty
}
/// Constructs a dictionary adopting the given storage.
@inlinable
internal init(_ storage: __owned __RawDictionaryStorage) {
self._storage = storage
}
@inlinable
internal init(capacity: Int) {
if capacity == 0 {
self._storage = __RawDictionaryStorage.empty
} else {
// allcocate 說(shuō)明_DictionaryStroage是一個(gè)類(lèi)
self._storage = _DictionaryStorage<Key, Value>.allocate(capacity: capacity)
}
}
}
internal class __RawDictionaryStorage: __SwiftNativeNSDictionary {
// NOTE: The precise layout of this type is relied on in the runtime to
// provide a statically allocated empty singleton. See
// stdlib/public/stubs/GlobalObjects.cpp for details.
/// The current number of occupied entries in this dictionary.
@usableFromInline
@nonobjc
internal final var _count: Int
/// The maximum number of elements that can be inserted into this set without
/// exceeding the hash table's maximum load factor.
@usableFromInline
@nonobjc
internal final var _capacity: Int
/// The scale of this dictionary. The number of buckets is 2 raised to the
/// power of `scale`.
@usableFromInline
@nonobjc
internal final var _scale: Int8
/// The scale corresponding to the highest `reserveCapacity(_:)` call so far,
/// or 0 if there were none. This may be used later to allow removals to
/// resize storage.
///
/// FIXME: <rdar://problem/18114559> Shrink storage on deletion
@usableFromInline
@nonobjc
internal final var _reservedScale: Int8
// Currently unused, set to zero.
@nonobjc
internal final var _extra: Int16
/// A mutation count, enabling stricter index validation.
@usableFromInline
@nonobjc
internal final var _age: Int32
/// The hash seed used to hash elements in this dictionary instance.
@usableFromInline
internal final var _seed: Int
/// A raw pointer to the start of the tail-allocated hash buffer holding keys.
@usableFromInline
@nonobjc
internal final var _rawKeys: UnsafeMutableRawPointer
/// A raw pointer to the start of the tail-allocated hash buffer holding
/// values.
@usableFromInline
@nonobjc
internal final var _rawValues: UnsafeMutableRawPointer
// This type is made with allocWithTailElems, so no init is ever called.
// But we still need to have an init to satisfy the compiler.
@nonobjc
internal init(_doNotCallMe: ()) {
_internalInvariantFailure("This class cannot be directly initialized")
}
@inlinable
@nonobjc
internal final var _bucketCount: Int {
@inline(__always) get { return 1 &<< _scale }
}
@inlinable
@nonobjc
internal final var _metadata: UnsafeMutablePointer<_HashTable.Word> {
@inline(__always) get {
let address = Builtin.projectTailElems(self, _HashTable.Word.self)
return UnsafeMutablePointer(address)
}
}
// The _HashTable struct contains pointers into tail-allocated storage, so
// this is unsafe and needs `_fixLifetime` calls in the caller.
@inlinable
@nonobjc
internal final var _hashTable: _HashTable {
@inline(__always) get {
return _HashTable(words: _metadata, bucketCount: _bucketCount)
}
}
}
}
經(jīng)過(guò)上面的分析我們可以得到如下的結(jié)論,在純Swift 的字典中其內(nèi)存結(jié)構(gòu)如下:
- Dictionary----->包含關(guān)聯(lián)值枚舉屬性_variant初始化的關(guān)聯(lián)值是_NativeDictionary
- _NativeDictionary是一個(gè)結(jié)構(gòu)體包含屬性 _storage,類(lèi)型是__RawDictionaryStorage,為Class
- __RawDictionaryStorage是一個(gè)類(lèi)型,初始化_storage的時(shí)候使用的是子類(lèi)_DictionaryStorage

自定義實(shí)現(xiàn)HashTable
struct HashTable<Key: Hashable, Value> {
typealias Element = (key: Key, value: Value)
var buckets:[bucket]
typealias bucket = [Element]
var count = 0
init(capacity: Int) {
buckets = Array<bucket>(repeating: bucket(), count: capacity)
}
//哈希函數(shù)
func index(key: Key) -> Int {
return abs(key.hashValue) % buckets.count
}
//dic[0]支持下標(biāo)訪(fǎng)問(wèn)需要自己實(shí)現(xiàn)subscript
subscript(key: Key) -> Value? {
get {
return getValue(key: key)
}
set {
if let value = newValue {
//更新
updateValue(value, key)
} else {
//沒(méi)有移除
removeValue(key: key)
}
}
}
func getValue(key: Key) -> Value? {
let index = self.index(key: key)
for element in buckets[index] {
if element.key == key {
return element.value
}
}
return nil
}
mutating func updateValue(_ value: Value, _ key : Key) -> Value? {
let index = self.index(key: key)
for (i,element) in buckets[index].enumerated() {
if element.key == key {
buckets[index][i] = (key: key, value: value)
count += 1
return element.value
}
}
buckets[index].append((key: key, value: value))
count += 1
return nil
}
mutating func removeValue(key : Key) -> Value? {
let index = self.index(key: key)
for (i,element) in buckets[index].enumerated() {
if element.key == key {
buckets[index].remove(at: i)
count -= 1
return element.value
}
}
return nil
}
}
var b = HashTable<String, String>(capacity: 4)
b["a"] = "鳥(niǎo)"
print(b["a"])
b["a"] = "貓"
print(b["a"])
b["a"] = nil
print(b["a"])
b.updateValue("虎", "b")
b.updateValue("虎", "c")
b.updateValue("虎", "d")
print(b.getValue(key: "b"))
print(b.getValue(key: "c"))
print(b.getValue(key: "d"))
b.updateValue("超了", "e")
print(b.getValue(key: "e"))

