Swift進(jìn)階-類與結(jié)構(gòu)體
Swift-函數(shù)派發(fā)
Swift進(jìn)階-屬性
Swift進(jìn)階-指針
Swift進(jìn)階-內(nèi)存管理
Swift進(jìn)階-TargetClassMetadata和TargetStructMetadata數(shù)據(jù)結(jié)構(gòu)源碼分析
Swift進(jìn)階-Mirror解析
Swift進(jìn)階-閉包
Swift進(jìn)階-協(xié)議
Swift進(jìn)階-泛型
Swift進(jìn)階-String源碼解析
Swift進(jìn)階-Array源碼解析
為什么會(huì)有泛型?
func multiNumInt(_ x: Int, _ y: Int) -> Int {
return x * y
}
func multiNumDouble(_ x: Double, _ y: Double) -> Double {
return x * y
}
我們發(fā)現(xiàn), multiNumInt(::) 、 multiNumDouble(::) 函數(shù)體是一樣的。唯一的區(qū)別是它們接收值類型不同( Int 、 Double )。
這個(gè)時(shí)候我們想找到一個(gè)可以計(jì)算任意類型值的函數(shù)怎么辦?泛型正是能讓我們寫出這樣函數(shù)的語法。
一、泛型語法
首先我我們要指定一個(gè)占位符 T ,緊挨著寫在函數(shù)名后面的 一對(duì)尖括號(hào)(當(dāng)前我們這個(gè) T 要遵循 FloatingPoint 協(xié)議,計(jì)算乘積所必須);其次我們就可以使用 T 來替換任意定義的函數(shù)形式參數(shù)。
- 舉例一:函數(shù)的泛型
func multiNum<T: FloatingPoint>(_ x: T, _ y: T) -> T {
return x * y
}
- 舉例二:類型的泛型
struct Stack {
private var items = [Int]()
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int? {
if items.isEmpty { return nil }
return items.removeLast()
}
}
這是一個(gè)標(biāo)準(zhǔn)的非泛型版的數(shù)據(jù)結(jié)構(gòu),如果我們想要改造一下怎么做:
struct Stack<Element> {
private var items = [Element]()
mutating func push(_ item: Element){
items.append(item)
}
mutating func pop() -> Element? {
if items.isEmpty { return nil }
return items.removeLast()
}
}
- 舉例三: 協(xié)議的泛型
基本上和棧的數(shù)據(jù)結(jié)構(gòu)相關(guān)都有相同的操作,這個(gè)時(shí)候我們免不了抽取相同的行為定一個(gè)協(xié)議:
protocol StackProtocol {
var itemCount: Int{ get }
mutating func pop() -> Int?
func index(of index: Int) -> Int
}
定義協(xié)議的時(shí)候需要指明當(dāng)前類型,那么能不能給 Protocol 也上一個(gè)泛型?系統(tǒng)提示 Protocol 不支持泛型參數(shù),需要我們使用關(guān)聯(lián)類型來代替。
protocol StackProtocol {
associatedtype Item
var itemCount: Item{ get }
mutating func pop() -> Item?
func index(of index: Int) -> Item
}
-
案例四: 關(guān)聯(lián)類型給協(xié)議中用到的類型一個(gè)占位符名稱
協(xié)議定義中的相關(guān)類型我們都可以用這個(gè)占位符替代,等到真正實(shí)現(xiàn)協(xié)議的時(shí)候在去確定當(dāng)前占位符的類型,比如下面的代碼:
protocol StackProtocol {
associatedtype Item
var itemCount: Int{ get }
mutating func pop() -> Item?
func index(of index: Int) -> Item
}
struct Stack: StackProtocol {
typealias Item = Int
private var items = [Item]()
var itemCount: Int{
get{
return items.count
}
}
mutating func push(_ item: Item) {
items.append(item)
}
mutating func pop() -> Item? {
if items.isEmpty { return nil }
return items.removeLast()
}
func index(of index: Int) -> Item {
return items[index]
}
}
- 案例五:協(xié)議繼承聲明
同樣的我們也可以給當(dāng)前的關(guān)聯(lián)類型添加約束,比如我們要求 Item 必須都要遵循 FixWidthInteger:
protocol StackProtocol {
associatedtype Item: FixedWidthInteger
var itemCount: Item{ get }
mutating func pop() -> Item?
func index(of index: Int) -> Item
}
當(dāng)然我們也可以直接在約束中使用協(xié)議
protocol EvenProtocol: StackProtocol {
associatedtype Even: EvenProtocol where Even.Item == Item
func pushEven(_ item: Int) -> Even
}
在這個(gè)協(xié)議里, Even 是一個(gè)關(guān)聯(lián)類型,就像上邊例子中 StackProtocol 的 Item 類型一樣。
Even 擁有兩個(gè)約束:
1.它必須遵循 EvenProtocol協(xié)議(就是當(dāng)前定義的協(xié)議);
2.以及它的 Item 類型必須是和容器里的 Item 類型相同。
Item 的約束是一個(gè) where 分句。
protocol StackProtocol {
associatedtype Item: FixedWidthInteger
var itemCount: Item{ get }
mutating func pop() -> Item?
func index(of index: Int) -> Item
}
protocol EvenProtocol: StackProtocol {
associatedtype Even: EvenProtocol where Even.Item == Item
func pushEven(_ item: Int) -> Even
}
struct Stack: StackProtocol {
typealias Item = Int
private var items = [Item]()
var itemCount: Int{
get{
return items.count
}
}
mutating func push(_ item: Item) {
items.append(item)
}
mutating func pop() -> Item? {
if items.isEmpty { return nil }
return items.removeLast()
}
func index(of index: Int) -> Item {
return items[index]
}
}
extension Stack: EvenProtocol {
func pushEven(_ item: Int) -> Stack {
var result = Stack()
if item % 2 == 0 {
result.push(item)
}
return result
}
}
在上面的例子中我們出現(xiàn)了一個(gè)where 分句,泛型 Where 分句要求了關(guān)聯(lián)類型必須遵循指定的協(xié)議,或者指定的類型形式參數(shù)和關(guān)聯(lián)類型必須相同。
泛型 Where分句以 Where 關(guān)鍵字開頭,后接關(guān)聯(lián)類型的約束或類型和關(guān)聯(lián)類型一致的關(guān)系。
- 案例六:函數(shù)泛型約束
protocol StackProtocol {
associatedtype Item: FixedWidthInteger
var itemCount: Item { get }
mutating func pop() -> Item?
func index(of index: Int) -> Item
}
func compare<T1: StackProtocol, T2: StackProtocol>(_ stack1: T1, _ stack2: T2) -> Bool {
guard stack1.itemCount == stack2.itemCount else { return false }
for i in 0..<stack1.itemCount {
if stack1.index(of: Int(i)) != stack2.index(of: Int(i)) {
return false
}
}
return true
}
這個(gè)函數(shù)有兩個(gè)形式參數(shù), stack1 和 stack2 。 stack1 形式參數(shù)是 T1 類型, stack2 形式參數(shù)是 T2 類型。 T1 和 T2 是兩個(gè)容器類型的類型形式參數(shù),它們的類型在調(diào)用函數(shù)時(shí)決定。
下面是函數(shù)的兩個(gè)類型形式參數(shù)上設(shè)置的要求:
1、 T1 必須遵循 StackProtocol 協(xié)議(寫作 T1: StackProtocol );
2、 T2 也必須遵循 StackProtocol 協(xié)議(寫作 C2: StackProtocol );
3、 T1 的 Item 必須和 T2 的 Item 相同(寫作 T1.Item == C2.Item);
4、 T1 的 Item 必須遵循 Equatable 協(xié)議(寫作 T1.ItemType: Equatable )。
前兩個(gè)要求定義在了函數(shù)的類型形式參數(shù)列表里,后兩個(gè)要求定義在了函數(shù)的泛型 Where 分句中。
這些要求意味著:
1、 stack1 是一個(gè) T1 類型的容器;
2、 stack2 是一個(gè) T2 類型的容器;
3、 stack1 和 stack2 中的元素類型相同
二、類型擦除
給一個(gè)類型耦合的例子:
// 定義數(shù)據(jù)拉取協(xié)議
protocol DataFetch {
associatedtype DataType
func fetch(completion: ((Result<DataType, Error>)->Void))
}
// 數(shù)據(jù)模型
struct User {
let userId: Int
let name: String
}
// 定義數(shù)據(jù)拉取工具類 - 并且確定拉取后通過閉包返回User模型
struct UserDataFetch: DataFetch {
typealias DataType = User
// 類似網(wǎng)路網(wǎng)絡(luò)請(qǐng)求,獲取數(shù)據(jù)
func fetch(completion: ((Result<DataType, Error>) -> Void)) {
let user = User(userId: 1001, name: "安安")
completion(.success(user))
}
}
接下來看看ViewController會(huì)耦合的地方:
class MyViewController {
let dataFetch: UserDataFetch
init(_ dataFetch: UserDataFetch) {
self.dataFetch = dataFetch
}
}
可以看到ViewController里面聲明了一個(gè)UserDataFetch實(shí)例,那就會(huì)出現(xiàn)一種情況是那如果我又有一個(gè)VipUserDataFetch類型呢,那就又得嵌套進(jìn)了ViewController了,那就會(huì)顯得臃腫且耦合,那如果我們聲明成DataFetch協(xié)議類型呢?
因?yàn)槲覀兟暶魇且粋€(gè)泛型協(xié)議,會(huì)報(bào)錯(cuò)的:

想要解決上面的耦合性,又想VC傳入的時(shí)候使用泛型協(xié)議且又想繞過編譯器的檢查,可以抽象出一層中間層AnyDataFetch通過依賴注入的方式把協(xié)議的具體類型傳遞進(jìn)來了,并讓其做一個(gè)泛型約束:
- 這里我們定義了一個(gè)中間層結(jié)構(gòu)體
AnyDataFetch,AnyDataFetch實(shí)現(xiàn)了DataFetch的所有方法。 - 在
AnyDataFetch的初始化過程中,實(shí)現(xiàn)協(xié)議的類型會(huì)被當(dāng)做參數(shù)傳入(依賴注入) - 在
AnyDataFetch實(shí)現(xiàn)的具體協(xié)議方法fetch中,再轉(zhuǎn)發(fā)實(shí)現(xiàn)協(xié)議的抽象類型。
// 定義一個(gè)中間層處理vc數(shù)據(jù)接收,因?yàn)関c接收的不一定只有 UserData 獲取的數(shù)據(jù),也可以VipUserData等等...
struct AnyDataFetch<T>: DataFetch {
typealias DataType = T
private let _fetch: ((Result<T, Error>) -> Void) -> Void
init<U: DataFetch>(_ fetchable: U) where U.DataType == T {
self._fetch = fetchable.fetch
}
func fetch(completion: ((Result<T, Error>) -> Void)) {
_fetch(completion)
}
}
class MyViewController {
let dataFetch: AnyDataFetch<User>
init(_ dataFetch: AnyDataFetch<User>) {
self.dataFetch = dataFetch
}
}
let userData = UserDataFetch()
let anyDataFetch = AnyDataFetch<User>(userData)
let myVC = MyViewController(anyDataFetch)
myVC.dataFetch.fetch { result in
switch result {
case .success(let user):
print(user.name)
case .failure(let error):
print(error)
}
}
這里可能大家看不出有什么區(qū)別,這樣做的好處就是我們不用知道當(dāng)前請(qǐng)求的具體類型是什么, 也就意味著如果我改變了之后,我還有一個(gè)VipDataFetch遵循了DataFetch協(xié)議:
struct VipDataFetch: DataFetch {
typealias DataType = User
func fetch(completion: ((Result<DataType, Error>) -> Void)) {
let user = User(userId: 0001, name: "VIP")
completion(.success(user))
}
}
let vipDataFetch = VipDataFetch()
let anyDataFetch1 = AnyDataFetch<User>(vipDataFetch)
let vc = MyViewController(anyDataFetch1)
vc.dataFetch.fetch { (result) in
switch result {
case .success(let user):
print(user.name)
case .failure(let error):
print(error)
}
}
對(duì)于VC來說壓根兒不關(guān)心具體的DataFetch類型了,關(guān)注點(diǎn)只放在model上。
系統(tǒng)上對(duì)于類型擦除的運(yùn)用比如:AnyCollection、AnySequence
AnySequence使用案例
假設(shè)你有這樣一個(gè)需求,你需要迭代你的自定義屬性 User ;此刻對(duì)于User 來說,我們應(yīng)該要做的是實(shí)現(xiàn) Sequence 協(xié)議:
struct User: Sequence {
var userId: Int
var name: String
func makeIterator() -> CustomIterator {
return CustomIterator(obj: self)
}
}
struct CustomIterator: IteratorProtocol {
var children: Mirror.Children
init(obj: Any) {
children = Mirror(reflecting: obj).children
}
mutating func next() -> String? {
guard let child = children.popFirst() else { return nil }
return "\(child.label ?? "無label") is \(child.value)"
}
}
那這個(gè)時(shí)候,對(duì)于我們當(dāng)前的另一個(gè)頁面的 VIP 用戶數(shù)據(jù),也需要遍歷,對(duì)于 VIP 和 USer 來說他們的行為是一致的,所以這里我們希望抽象出一個(gè)統(tǒng)一的協(xié)議:
struct VIP: CustomDataSeuqence{
var vipdate: String = "20221226"
var viplevel: Int = 18
var vipName: String = "林林"
}
protocol CustomDataSeuqence: Sequence {}
extension CustomDataSeuqence {
func makeIterator() -> CustomIterator {
return CustomIterator(obj: self)
}
}
接下來比如我們要做一個(gè)社群功能,需要當(dāng)前同一個(gè)社群當(dāng)中的用戶進(jìn)行遍歷,如果是VIP,需要特殊顯示一些效果。
那么這里的 Users 應(yīng)該定義成什么類型呢?定義成 Any 類型,這個(gè)時(shí)候?qū)τ诋?dāng)前頁面來說就需要講 Any 的類型強(qiáng)轉(zhuǎn)成 CustomDataSeuqence ,可以,但是有點(diǎn)牽強(qiáng)。這個(gè)時(shí)候我們就可以使用 AnySeuqence
let user = User(userId: 1001, name: "安安")
let vip = VIP()
let users: [AnySequence<String>] = [AnySequence(user), AnySequence(vip)]
for user in users{
// print(user)
for item in user {
print(item)
}
}
打印結(jié)果:
userId is 1001
name is 安安
vipdate is 20221226
viplevel is 18
vipName is 林林
這個(gè)時(shí)候 AnySequence 就將具體的 Sequence 類型隱藏了,調(diào)用者只知道數(shù)組中的元素是一個(gè)可以迭代輸出字符串類型的序列。
三、泛型的底層原理
func testGenric<T>(_ value: T) -> T {
let tmp = value
return value
}
testGenric(10)
對(duì)于編譯器來說,它不知道T到底是什么具體的類型(可能是值類型,也可能是引用類型),那編譯器就不知道 tmp / value 到底分配多少內(nèi)存空間、步長是多少、對(duì)齊的字段是多少...只有當(dāng)真正傳遞參數(shù)才知道真實(shí)類型對(duì)吧。
那tmp在方法棧占據(jù)內(nèi)存空間編譯器是怎么決斷的呢?
來看看上面代碼編譯成IR代碼進(jìn)行分析:


可以看到在調(diào)用testGenric時(shí)候可以看到一個(gè)值見證表valueWitnesses,并且可以看出到initializeWithCopy、destroy等函數(shù)都是從值見證表來的,所以泛型是通過ValueWitnessTable去管理內(nèi)存的。

截圖是ValueWitnessTable的數(shù)據(jù)結(jié)構(gòu),前面幾個(gè)i8*其實(shí)就是void *代表內(nèi)存管理函數(shù),剩下的就是size、stride、flags、extraInhabitantCount,把數(shù)據(jù)結(jié)構(gòu)還原一下
ValueWitnessTable的數(shù)據(jù)結(jié)構(gòu):
// 弱引用表
struct ValueWitnessTable {
var unknow1: UnsafeRawPointer
var unknow2: UnsafeRawPointer
var unknow3: UnsafeRawPointer
var unknow4: UnsafeRawPointer
var unknow5: UnsafeRawPointer
var unknow6: UnsafeRawPointer
var unknow7: UnsafeRawPointer
var unknow8: UnsafeRawPointer
var size: Int
var stride: Int
var flags: UInt32
var extraInhabitantCount: UInt32
}
不管testGenric函數(shù)的參數(shù)傳遞的是值/引用類型,不管什么類型,總之它的metadata一定有ValueWitnessTable!并且保存著這個(gè)類型的size、stride、flags、extraInhabitantCount還有一些內(nèi)存管理函數(shù)等信息。
嘗試還原一下上面分析的結(jié)構(gòu)體對(duì)不對(duì),舉一個(gè)案例唄:
struct Teacher {
var age = 10
}
// 所有類型的最終基類 Metadata
struct TargetMetadata {
var kind: Int // kind用于區(qū)分類型的
}
ps: 不懂為啥是TargetMetadata可以看我之前f的分享的文章
let ptr = unsafeBitCast(Teacher.self as Any.Type, to: UnsafeMutablePointer<TargetMetadata>.self)
let valueWitnessTable = UnsafeRawPointer(ptr).advanced(by: -MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: UnsafeMutablePointer<ValueWitnessTable>.self).pointee
print(valueWitnessTable.pointee.size) // 8
// 如果給 Teacher 添加一個(gè)屬性 var age1 = 20,則打印的size就是 16
print(valueWitnessTable.pointee.unknow1) // 0x00007ff81b935400
print(valueWitnessTable.pointee.unknow2) // 0x00007ff81b935410
print(valueWitnessTable.pointee.unknow3) // 0x00007ff81b935420
print(valueWitnessTable.pointee.unknow4)
print(valueWitnessTable.pointee.unknow5)
print(valueWitnessTable.pointee.unknow6)
print(valueWitnessTable.pointee.unknow7)
print(valueWitnessTable.pointee.unknow8)
print("end")
ps: 為什么ValueWitnessTable是在TargetMetadata向前移動(dòng)?

在IR代碼中就能分析出,ValueWitnessTable的位置是在TargetMetadata的向前移動(dòng)-1的位置。
來看看unknow1打印的是什么?

得到函數(shù)名,可以自行打印最后恢復(fù)ValueWitnessTable的數(shù)據(jù)結(jié)構(gòu):
// 弱引用表
struct ValueWitnessTable {
var initializeBufferWithCopyOfBuffer: UnsafeRawPointer
var destroy: UnsafeRawPointer
var initializeWithCopy: UnsafeRawPointer
var assignWithCopy: UnsafeRawPointer
var initializeWithTake: UnsafeRawPointer
var assignWithTake: UnsafeRawPointer
var getEnumTagSinglePayload: UnsafeRawPointer
var storeEnumTagSinglePayload: UnsafeRawPointer
var size: Int
var stride: Int
var flags: UInt32
var extraInhabitantCount: UInt32
}
在來對(duì)比剛才testGenric 編譯后的IR代碼里的,如果我們上面的結(jié)論:不管什么類型它的metadata一定有ValueWitnessTable是對(duì)的,那一定可以在IR上找到上面結(jié)構(gòu)體里還原出來的函數(shù)。
恰好能對(duì)應(yīng)上面還原的方法名:

總結(jié):
- 泛型是通過
ValueWitnessTable(簡稱VWT是由編譯器產(chǎn)生的) 來進(jìn)行內(nèi)存管理,VWT存儲(chǔ)了size、stride、alignment和一些內(nèi)存管理函數(shù)等; - 對(duì)于值類型來說,實(shí)際上是通過內(nèi)存的拷貝;copy/move等操作;
- 對(duì)于引用類型來說,就是堆區(qū)內(nèi)存塊的引用,通過引用計(jì)數(shù)的方式。
如果傳遞的參數(shù)是泛型呢?會(huì)發(fā)生什么變化?
來看看下面這個(gè)案例
func makeIncrementer() -> (Int) -> Int {
var runningTotal = 10
func incrementer(mount: Int) -> Int {
runningTotal += mount
return runningTotal
}
return incrementer
}
func genric<T>(t: T) {
}
let increment = makeIncrementer()
genric(t: increment)
編譯成IR代碼,找到mian函數(shù):
define i32 @main(i32 %0, i8** %1) #0 {
entry:
%2 = alloca %swift.function, align 8
%3 = bitcast i8** %1 to i8*
// 調(diào)用makeIncrementer,生成閉包
%4 = call swiftcc { i8*, %swift.refcounted* } @"$s4main15makeIncrementerS2icyF"()
%5 = extractvalue { i8*, %swift.refcounted* } %4, 0
%6 = extractvalue { i8*, %swift.refcounted* } %4, 1
// 將指針存儲(chǔ)到increment變量里
store i8* %5, i8** getelementptr inbounds (%swift.function, %swift.function* @"$s4main9incrementyS2icvp", i32 0, i32 0), align 8
// 將捕獲的變量存儲(chǔ)到increment變量里
store %swift.refcounted* %6, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main9incrementyS2icvp", i32 0, i32 1), align 8
// 把function類型轉(zhuǎn)換成void *
%7 = bitcast %swift.function* %2 to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %7)
// 從increment變量取出指針和捕獲的變量
%8 = load i8*, i8** getelementptr inbounds (%swift.function, %swift.function* @"$s4main9incrementyS2icvp", i32 0, i32 0), align 8
%9 = load %swift.refcounted*, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main9incrementyS2icvp", i32 0, i32 1), align 8
// 一些內(nèi)存管理的函數(shù)
%10 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %9) #3
// swift_allocObject創(chuàng)建了堆區(qū)的內(nèi)存空間
%11 = call noalias %swift.refcounted* @swift_allocObject(%swift.type* getelementptr inbounds (%swift.full_boxmetadata, %swift.full_boxmetadata* @metadata, i32 0, i32 2), i64 32, i64 7) #3
%12 = bitcast %swift.refcounted* %11 to <{ %swift.refcounted, %swift.function }>*
// 取出第一個(gè)成員是function這個(gè)結(jié)構(gòu)體
%13 = getelementptr inbounds <{ %swift.refcounted, %swift.function }>, <{ %swift.refcounted, %swift.function }>* %12, i32 0, i32 1
// 從function這個(gè)結(jié)構(gòu)體取出函數(shù)地址
%.fn = getelementptr inbounds %swift.function, %swift.function* %13, i32 0, i32 0
store i8* %8, i8** %.fn, align 8
%.data = getelementptr inbounds %swift.function, %swift.function* %13, i32 0, i32 1
store %swift.refcounted* %9, %swift.refcounted** %.data, align 8
%.fn1 = getelementptr inbounds %swift.function, %swift.function* %2, i32 0, i32 0
store i8* bitcast (void (%TSi*, %TSi*, %swift.refcounted*)* @"$sS2iIegyd_S2iIegnr_TRTA" to i8*), i8** %.fn1, align 8
%.data2 = getelementptr inbounds %swift.function, %swift.function* %2, i32 0, i32 1
store %swift.refcounted* %11, %swift.refcounted** %.data2, align 8
%14 = bitcast %swift.function* %2 to %swift.opaque*
// 后面又對(duì)這個(gè)function又做了一層重新的抽象
%15 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$sS2icMD") #10
call swiftcc void @"$s4main6genric1tyx_tlF"(%swift.opaque* noalias nocapture %14, %swift.type* %15)
%.data3 = getelementptr inbounds %swift.function, %swift.function* %2, i32 0, i32 1
%16 = load %swift.refcounted*, %swift.refcounted** %.data3, align 8
call void @swift_release(%swift.refcounted* %16) #3
%17 = bitcast %swift.function* %2 to i8*
call void @llvm.lifetime.end.p0i8(i64 16, i8* %17)
ret i32 0
}
可以看到func genric<T>(t: T) 當(dāng)泛型參數(shù)接收一個(gè)閉包/函數(shù)時(shí),會(huì)對(duì)這個(gè)閉包/函數(shù)原有數(shù)據(jù)結(jié)構(gòu)上又抽象封裝了一層。因?yàn)槲覀儌魅肟赡苁鞘呛瘮?shù),也可能傳入的是閉包表達(dá)式,編譯器不知道呀,它就和協(xié)議設(shè)計(jì)一樣不知道誰遵循了協(xié)議,所以它就抽象了一層中間層為了泛型管理統(tǒng)一區(qū)分類型。
當(dāng)去調(diào)用的時(shí)候,是通過這個(gè)抽象出來的中間層來取調(diào)用,如果不調(diào)用而用來傳值的話,依舊是通過上面的ValueWitnessTable進(jìn)行內(nèi)存管理的。(注意:函數(shù)/閉包也是引用類型)。
func genric<T>(t: T) {
// VWT管理內(nèi)存
let tmp = t
// 函數(shù)調(diào)用,抽象中間層統(tǒng)一管理函數(shù)/閉包
var t1 = t as! (Int) -> Int
t1()
}
原本對(duì)于閉包的數(shù)據(jù)結(jié)構(gòu)是這樣的:(想了解可點(diǎn)擊 函數(shù)/閉包數(shù)據(jù)結(jié)構(gòu))
// 閉包的實(shí)質(zhì)
struct ClosureData<T> {
var ptr: UnsafeRawPointer // 閉包地址
var box: UnsafePointer<T> // Box
}
// 捕獲單個(gè)外部變量的時(shí)候Box
struct Box<T> {
var heapObject: HeapObject // 實(shí)例對(duì)象的內(nèi)存地址
var value: T
}
// 捕獲多個(gè)外部變量的時(shí)候Box
//struct Box<T1, T2> {
// var object: HeapObject // 把value1、value2統(tǒng)一當(dāng)成object的屬性
// var value1: UnsafePointer<T1>
// var value2: T2
// var value3.......
//}
struct HeapObject {
var metadata: UnsafeRawPointer
var refcount1: UInt32
var refcount2: UInt32
}
泛型參數(shù)接收閉包/函數(shù)后對(duì)其進(jìn)行調(diào)用,又抽象一層封裝的數(shù)據(jù)結(jié)構(gòu):
// 閉包作為泛型參數(shù),又包裝了一層
struct ReabstractionThunkContext<Context> {
var heapObject: HeapObject
var function: ClosureData<Context>
}
驗(yàn)證數(shù)據(jù)結(jié)構(gòu):
func makeIncrementer() -> (Int) -> Int {
var runningTotal = 10
func incrementer(mount: Int) -> Int {
runningTotal += mount
return runningTotal
}
return incrementer
}
func genric<T>(t: T) {
let ptr = UnsafeMutablePointer<T>.allocate(capacity: 1)
ptr.initialize(to: t)
defer {
ptr.deinitialize(count: 1)
ptr.deallocate()
}
let closure_ptr = ptr.withMemoryRebound(to: ClosureData<ReabstractionThunkContext<Box<Int>>>.self, capacity: 1) { $0 }
let ctx = closure_ptr.pointee.box.pointee.function.box
print(ctx.pointee.value) // 10 得到捕獲的外部變量的值
}
let increment = makeIncrementer()
genric(t: increment)