一: 泛型
1.1 什么是泛型
泛型可以將類型參數(shù)化,提高代碼復(fù)用效率,減少代碼量。
1.2 泛型解決的問題
下面是一個(gè)標(biāo)準(zhǔn)的非泛型函數(shù) swapTwoInts(_:_:),用來交換兩個(gè) Int 值:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let tempA = a
a = b
b = tempA
}
這個(gè)函數(shù)使用輸入輸出參數(shù)(inout)來交換 a 和 b 的值。 swapTwoInts(_:_:) 函數(shù)將 b 的原始值換成了 a,將 a 的原始值換成了 b,可以調(diào)用這個(gè)函數(shù)來交換兩個(gè) Int 類型變量
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
打印結(jié)果
someInt is now 107, and anotherInt is now 3
swapTwoInts(_:_:) 函數(shù)很實(shí)用,但它只能作用于 Int 類型。如果想交換兩個(gè) String 類型值,或者 Double 類型值,你必須編寫對應(yīng)的函數(shù),類似下面 swapTwoStrings(_:_:) 和 swapTwoDoubles(_:_:) 函數(shù):
func swapTwoStrings(_ a: inout String, _ b: inout String) {
let tempA = a
a = b
b = tempA
}
func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
let tempA = a
a = b
b = tempA
}
你可能注意到了,swapTwoInts(_:_:)、swapTwoStrings(_:_:) 和 swapTwoDoubles(_:_:) 函數(shù)體是一樣的,唯一的區(qū)別是它們接受的參數(shù)類型(Int、String 和 Double)。
在實(shí)際應(yīng)用中,通常需要一個(gè)更實(shí)用更靈活的函數(shù)來交換兩個(gè)任意類型的值,幸運(yùn)的是,泛型代碼就是為了解決這種問題而存在。
1.3 泛型基本語法
1.3.1 泛型函數(shù)
首先要指定一個(gè)占位符 T ,緊挨著寫在函數(shù)名后面的一對尖括號(hào),其次我們就可以使用 T 來替換任意定義的函數(shù)形式參數(shù)
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let tempA = a
a = b
b = tempA
}
泛型版本的函數(shù)使用 占位符 類型名(這里叫做 T ),而不是 實(shí)際類型名(例如 Int、String 或 Double ),占位符類型名并不關(guān)心 T 具體的類型,但它要求 a 和 b 必須是相同的類型,T 的實(shí)際類型由每次調(diào)用 swapTwoValues(_:_:) 來決定。
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
print("someString is now \(someString), and anotherString is now \(anotherString)")
打印結(jié)果
someInt is now 107, and anotherInt is now 3
someString is now world, and anotherString is now hello
1.3.2 泛型類型
struct IntStack {
var items: [Int] = []
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
}
這個(gè)結(jié)構(gòu)體在棧中使用一個(gè)名為 items 的數(shù)組屬性來存儲(chǔ)值。棧提供了兩個(gè)方法:push(_:) 和 pop() ,用來向棧中壓入值以及從棧中移除值。這些方法被標(biāo)記為 mutating ,因?yàn)樗鼈冃枰薷慕Y(jié)構(gòu)體的 items 數(shù)組。
上面的 IntStack 結(jié)構(gòu)體只能用于 Int 類型。不過,可以定義一個(gè)泛型 Stack 結(jié)構(gòu)體,從而能夠處理任意類型的值。
struct Stack<Element> {
var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
注意,Stack 基本上和 IntStack 相同,只是用占位類型參數(shù) Element 代替了實(shí)際的 Int 類型。這個(gè)類型參數(shù)包裹在緊隨結(jié)構(gòu)體名的一對尖括號(hào)里( <Element> )。
Element 為待提供的類型定義了一個(gè)占位名。這種待提供的類型可以在結(jié)構(gòu)體的定義中通過 Element 來引用。
在這個(gè)例子中,Element 在如下三個(gè)地方被用作占位符:
- 創(chuàng)建
items屬性,使用Element類型的空數(shù)組對其進(jìn)行初始化。 - 指定
push(_:)方法的唯一參數(shù)item的類型必須是Element類型。 - 指定
pop()方法的返回值類型必須是Element類型。
由于Stack是泛型類型,因此可以用來創(chuàng)建適用于Swift中任意有效類型的棧,就像Array和Dictionary那樣。
可以通過在尖括號(hào)中寫出棧中需要存儲(chǔ)的數(shù)據(jù)類型來創(chuàng)建并初始化一個(gè)Stack實(shí)例。例如,要?jiǎng)?chuàng)建一個(gè)String類型的棧,可以寫成Stack<String>():
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
1.3.3 關(guān)聯(lián)類型
對于協(xié)議來說是無法像函數(shù)、類型那樣直接在協(xié)議名后添加 <T> 那樣使用泛型,需要我們使用關(guān)聯(lián)類型來代替。關(guān)聯(lián)類型通過 associatedtype 關(guān)鍵字來指定。
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
關(guān)聯(lián)類型為協(xié)議中的某個(gè)類型提供了一個(gè)占位符名稱,其代表的實(shí)際類型在協(xié)議被遵循時(shí)才會(huì)被指定。
struct IntStack: Container {
// IntStack 的原始實(shí)現(xiàn)部分
var items: [Int] = []
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// Container 協(xié)議的實(shí)現(xiàn)部分
typealias Item = Int
mutating func append(_ item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
1.3.4 類型約束
我們可以給泛型 T 添加特定的類型約束,這將在某些情況下非常有用。類型約束指定類型參數(shù)必須繼承自指定類、遵循特定的協(xié)議或協(xié)議組合。
protocol myProtocol {
func protocolFunc()
}
func test<T: myProtocol> (_ value1: T, _ value2: T) {
value1.protocolFunc()
value2.protocolFunc()
}
也可以在協(xié)議里給關(guān)聯(lián)類型添加約束來要求遵循的類型滿足約束
protocol Container {
associatedtype Item: Equatable
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
要遵守 Container 協(xié)議,Item 類型也必須遵守 Equatable 協(xié)議。
協(xié)議可以作為它自身的要求出現(xiàn)。例如,有一個(gè)協(xié)議細(xì)化了 Container 協(xié)議,添加了一個(gè) suffix(_:) 方法。
protocol SuffixContainer: Container {
associatedtype Suffix: SuffixContainer where Suffix.Item == Item
func suffix(_ size: Int) -> Suffix
}
在這個(gè)協(xié)議里,Suffix 是一個(gè)關(guān)聯(lián)類型,就像上邊例子中 Container 的 Item 類型一樣。Suffix 擁有兩個(gè)約束:它必須遵循 SuffixContainer 協(xié)議(就是當(dāng)前定義的協(xié)議),以及它的 Item 類型必須是和容器里的 Item 類型相同。Item 的約束是一個(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ù),是將具體類型的類型信息擦除掉,只將類型的抽象信息,通常指的是類型尊從的協(xié)議、接口、或基類暴露出來。我們通過下面這個(gè)案例進(jìn)一步了解什么是類型擦除。
// 定義一個(gè)協(xié)議: 用于數(shù)據(jù)提取
protocol DataFetch {
associatedtype dataType
// 數(shù)據(jù)提取方法
func fetchData(completion: ((Result<dataType, Error>) -> Void)?)
}
這里有一個(gè)協(xié)議 DataFetch,協(xié)議里有一個(gè)關(guān)聯(lián)類型 dataType,這個(gè)協(xié)議用來提取數(shù)據(jù)用的,它聲明里一個(gè)提取數(shù)據(jù)的方法 fetchData(completion:) 這里傳入的是一個(gè)閉包 (Result<dataType, Error>) -> Void) 接收一個(gè) Result 類型的參數(shù),并且第一個(gè)參數(shù)就是泛型協(xié)議的關(guān)聯(lián)類型 dataType ,因?yàn)槲覀儾恢捞崛〕鰜淼臄?shù)據(jù)是字典類型、字符串類型還是數(shù)組類型或者是其它類型。
struct UserModel {
let uId: Int
let userType: String
}
// 定義一個(gè)UserData 準(zhǔn)守泛型協(xié)議 DataFetch 并且實(shí)現(xiàn)了其中的方法 fetchData 返回一個(gè) User的數(shù)據(jù)模型
struct UserData: DataFetch {
typealias dataType = UserModel
func fetchData(completion: ((Result<UserModel, Error>) -> Void)?) {
let user = UserModel(uId: 10001, userType: "普通會(huì)員")
completion?(.success(user))
}
}
// 定義一個(gè)VipUserData 準(zhǔn)守泛型協(xié)議 DataFetch 并且實(shí)現(xiàn)了其中的方法 fetchData 返回一個(gè) User的數(shù)據(jù)模型
struct VipUserData: DataFetch {
typealias dataType = UserModel
func fetchData(completion: ((Result<UserModel, Error>) -> Void)?) {
let user = UserModel(uId: 10001, userType: "高級(jí)會(huì)員")
completion?(.success(user))
}
}
這里我定義了兩個(gè)類型 UserData 和 VipUserData 都準(zhǔn)守了 DataFetch 協(xié)議并都實(shí)現(xiàn)了協(xié)議中的方法 fetchData(completion:) 但是返回的 UserModel 中的內(nèi)容不一致,一個(gè)是普通會(huì)員,一個(gè)是高級(jí)會(huì)員。
當(dāng)一個(gè)類中包含了一個(gè)遵守 DataFetch 協(xié)議類型的變量,但這個(gè)變量的類型并不是單一的,而希望它支持遵守了 DataFetch 協(xié)議的其它類型,因?yàn)槲覀儾恍枰P(guān)心數(shù)據(jù)是如何提取的,只關(guān)心提取數(shù)據(jù)后的結(jié)構(gòu)。此時(shí)我們把這個(gè)變量當(dāng)作該類的一個(gè)屬性或者一個(gè)方法中的參數(shù),第一時(shí)間想到的是用 DataFetch 作為類型
class homeVC {
let userData: DataFetch
init(_ userData: DataFetch){
self.userData = userData
}
func setBaseData() {
self.userData.fetchData { (result) in
switch result {
case .success(let user):
print(user.userType)
case .failure(let error):
print(error)
}
}
}
}
此時(shí)編譯器去報(bào)錯(cuò)了

這里報(bào)錯(cuò)是因?yàn)椋簠f(xié)議
DataFetch 只能用作泛型約束,不能用作具體類型。因?yàn)榫幾g器無法確定關(guān)聯(lián)類型 dataType 的具體類型是什么。如果直接將 userData 的類型改成 UserData ,從某些角度來看是這么做是可以的,但是這會(huì)在 VC 和 UserData 對象之間創(chuàng)建一個(gè)依賴關(guān)系。如果我們遵循 SOLID 原則(簡單地說:接口職責(zé)應(yīng)該單一,不要承擔(dān)過多的職責(zé)。),我們希望避免依賴并隱藏實(shí)現(xiàn)細(xì)節(jié)。因此這里使用 類型擦除 技術(shù)。我們就需要引入一個(gè)中間層
struct AnyDataFetch<T>: DataFetch {
typealias dataType = T
private let _fetchData: (((Result<T, Error>) -> Void)?) -> Void
init<U: DataFetch>(_ fetchable: U) where U.dataType == T {
_fetchData = fetchable.fetchData(completion:)
}
func fetchData(completion: ((Result<T, Error>) -> Void)?) {
_fetchData(completion)
}
}
- 這里我們定義了一個(gè)中間層結(jié)構(gòu)體
AnyDataFetch,AnyDataFetch實(shí)現(xiàn)了DataFetch的所有方法。 - 在
AnyDataFetch的初始化過程中,實(shí)現(xiàn)協(xié)議的類型會(huì)被當(dāng)做參數(shù)傳入(依賴注入) - 在
AnyDataFetch實(shí)現(xiàn)的具體協(xié)議方法fetchData中,再轉(zhuǎn)發(fā)實(shí)現(xiàn)協(xié)議的抽象類型。
這個(gè)時(shí)候我們就可以把AnyDataFetch當(dāng)做具體類型使用。
class homeVC {
let userData: AnyDataFetch<UserModel>
init(_ userData: AnyDataFetch<UserModel>){
self.userData = userData
}
func setBaseData() {
self.userData.fetchData { (result) in
switch result {
case .success(let user):
print(user.userType)
case .failure(let error):
print(error)
}
}
}
}
let userData = UserData()
let anyDataFetch = AnyDataFetch<UserModel>(userData)
let vc = homeVC.init(anyDataFetch)
vc.setBaseData()
print("-----------")
let vipUserData = VipUserData()
let vipAnyDataFetch = AnyDataFetch<UserModel>(vipUserData)
let vipVC = homeVC.init(vipAnyDataFetch)
vipVC.setBaseData()
打印結(jié)果:
普通會(huì)員
-----------
高級(jí)會(huì)員
這樣做的好處就是對與 homeVC 來說不用知道當(dāng)前請求的具體類型是什么( 可以是 UserData 也可以是 VipUserData ) , homeVC 接收的其實(shí)就只是 AnyDataFetch<UserModel> 類型,這其實(shí)就是所謂的 類型擦除 。當(dāng)有另一個(gè)協(xié)議的抽象類型 ( superVipUserData ) 的時(shí)候,我們不需要改變 homeVC 的代碼,不需要改變 AnyDataFetch 的代碼。
系統(tǒng)中的 AnySequence , AnyCollection 都是這樣的原理。
三: 泛型的內(nèi)存結(jié)構(gòu)
3.1: 泛型內(nèi)存結(jié)構(gòu)分析
在 Swift探索(七): 閉包 中我們還原了函數(shù)的內(nèi)存結(jié)構(gòu),那么在今天這篇文章中加上泛型的函數(shù)的內(nèi)存結(jié)構(gòu)又是什么樣的呢?
func test <T>(_ value: T) -> T{
let temp = value;
return temp
}
test(10)
通過 swiftc main.swift -emit-ir > ./main.ll 編譯成 IR 文件,并且定位到 test() 函數(shù)的調(diào)用
define hidden swiftcc void @"$s4main4testyxxlF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %0, %swift.opaque* noalias nocapture %1, %swift.type* %T) #0 {
entry:
%T1 = alloca %swift.type*, align 8
%temp.debug = alloca i8*, align 8
%2 = bitcast i8** %temp.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
%value.debug = alloca %swift.opaque*, align 8
%3 = bitcast %swift.opaque** %value.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 8, i1 false)
store %swift.type* %T, %swift.type** %T1, align 8
%4 = bitcast %swift.type* %T to i8***
%5 = getelementptr inbounds i8**, i8*** %4, i64 -1
%T.valueWitnesses = load i8**, i8*** %5, align 8, !invariant.load !34, !dereferenceable !35
%6 = bitcast i8** %T.valueWitnesses to %swift.vwtable*
%7 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %6, i32 0, i32 8
%size = load i64, i64* %7, align 8, !invariant.load !34
%8 = alloca i8, i64 %size, align 16
call void @llvm.lifetime.start.p0i8(i64 -1, i8* %8)
%9 = bitcast i8* %8 to %swift.opaque*
store i8* %8, i8** %temp.debug, align 8
store %swift.opaque* %1, %swift.opaque** %value.debug, align 8
%10 = getelementptr inbounds i8*, i8** %T.valueWitnesses, i32 2
%11 = load i8*, i8** %10, align 8, !invariant.load !34
%initializeWithCopy = bitcast i8* %11 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)*
%12 = call %swift.opaque* %initializeWithCopy(%swift.opaque* noalias %9, %swift.opaque* noalias %1, %swift.type* %T) #3
%13 = call %swift.opaque* %initializeWithCopy(%swift.opaque* noalias %0, %swift.opaque* noalias %9, %swift.type* %T) #3
%14 = getelementptr inbounds i8*, i8** %T.valueWitnesses, i32 1
%15 = load i8*, i8** %14, align 8, !invariant.load !34
%destroy = bitcast i8* %15 to void (%swift.opaque*, %swift.type*)*
call void %destroy(%swift.opaque* noalias %9, %swift.type* %T) #3
%16 = bitcast %swift.opaque* %9 to i8*
call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16)
ret void
}
-
define hidden swiftcc void @"$s4main4testyxxlF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %0, %swift.opaque* noalias nocapture %1, %swift.type* %T) #0 {這里的%swift.type* %T就是傳入進(jìn)來的泛型T的類型,也就是在調(diào)用時(shí),是什么類型這里就是什么類型。 -
%T1 = alloca %swift.type*, align 8之前的文章當(dāng)中提到過swift.type類型,其實(shí)就是heapObject結(jié)構(gòu)體。 -
store %swift.type* %T, %swift.type** %T1, align 8將T存儲(chǔ)到T1中,這也就說明了不管是分配內(nèi)存空間還是管理這個(gè)值的內(nèi)存,都是依賴于當(dāng)前的類型的Metadata。 -
%5 = getelementptr inbounds i8**, i8*** %4, i64 -1取出-1位置的成員 -
%T.valueWitnesses = load i8**, i8*** %5, align 8, !invariant.load !34, !dereferenceable !35上面取出的成員就是valueWitnesses -
%6 = bitcast i8** %T.valueWitnesses to %swift.vwtable*轉(zhuǎn)成成%swift.vwtable結(jié)構(gòu)體
剩下的代碼就是處理%swift.vwtable里的東西。
通過IR代碼我們不難看出泛型函數(shù)中的泛型是通過%swift.vwtable來進(jìn)行管理內(nèi)存的。
3.2 ValueWitnessTable 值見證表
在 IR 代碼的最上面我們可以看到 %swift.vwtable 的結(jié)構(gòu)如下
%swift.vwtable = type { i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i64, i64, i32, i32 }
這里的 i8* 可以把它當(dāng)作 void*,也就是這些 i8* 其實(shí)就是當(dāng)前所謂的函數(shù),根據(jù)這個(gè)結(jié)構(gòu)體可以還原出 ValueWitnessTable
struct ValueWitnessesTable {
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 unknow9: Int64
var unknow10: Int64
var unknow11: Int32
var unknow12: Int32
}
通過查閱各種資料后最后得出
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
}
綜上可知泛型函數(shù)中的泛型不管是 值類型 還是 引用類型 他的內(nèi)存結(jié)構(gòu)中都是有 ValueWitnessTable , 并且是在 metadata 的前面,ValueWitnessTable 保存著這個(gè)類型的 size、stride、flags、extraInhabitantCount 還有一些 內(nèi)存管理函數(shù) 等信息。
3.3 函數(shù)(閉包)作為泛型參數(shù)
如果是函數(shù)或者閉包做為參數(shù)傳入到泛型函數(shù)里,又會(huì)不會(huì)有什么不一樣的嗎?
func makeIncrementer() -> () -> Void {
var runningTotal = 10
func incrementer() {
runningTotal += 10
}
return incrementer
}
func test <T>(_ value: T){
}
let f = makeIncrementer()
test(f)
同樣編譯成 IR 代碼定位到 main 函數(shù)的調(diào)用
define i32 @main(i32 %0, i8** %1) #0 {
entry:
%2 = alloca %swift.function, align 8
%3 = bitcast i8** %1 to i8*
%4 = call swiftcc { i8*, %swift.refcounted* } @"$s4main15makeIncrementeryycyF"()
%5 = extractvalue { i8*, %swift.refcounted* } %4, 0
%6 = extractvalue { i8*, %swift.refcounted* } %4, 1
store i8* %5, i8** getelementptr inbounds (%swift.function, %swift.function* @"$s4main1fyycvp", i32 0, i32 0), align 8
store %swift.refcounted* %6, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main1fyycvp", i32 0, i32 1), align 8
%7 = bitcast %swift.function* %2 to i8*
call void @llvm.lifetime.start.p0i8(i64 16, i8* %7)
%8 = load i8*, i8** getelementptr inbounds (%swift.function, %swift.function* @"$s4main1fyycvp", i32 0, i32 0), align 8
%9 = load %swift.refcounted*, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main1fyycvp", i32 0, i32 1), align 8
%10 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %9) #3
%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 }>*
%13 = getelementptr inbounds <{ %swift.refcounted, %swift.function }>, <{ %swift.refcounted, %swift.function }>* %12, i32 0, i32 1
%.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 (%swift.opaque*, %swift.refcounted*)* @"$sIeg_ytIegr_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*
%15 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$syycMD") #10
call swiftcc void @"$s4main4testyyxlF"(%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
}
%4 = call swiftcc { i8*, %swift.refcounted* } @"$s4main15makeIncrementeryycyF"()
%5 = extractvalue { i8*, %swift.refcounted* } %4, 0
%6 = extractvalue { i8*, %swift.refcounted* } %4, 1這三句代碼在之前的 Swift探索(七): 閉包 探究過 就是創(chuàng)建當(dāng)前的閉包表達(dá)式store i8* %5, i8** getelementptr inbounds (%swift.function, %swift.function* @"$s4main1fyycvp", i32 0, i32 0), align 8
store %swift.refcounted* %6, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main1fyycvp", i32 0, i32 1), align 8
%7 = bitcast %swift.function* %2 to i8*這三句就是存儲(chǔ)指針和捕獲的變量到f里并將f轉(zhuǎn)換成void*類型%8 = load i8*, i8** getelementptr inbounds (%swift.function, %swift.function* @"$s4main1fyycvp", i32 0, i32 0), align 8
%9 = load %swift.refcounted*, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main1fyycvp", i32 0, i32 1), align 8去取f這個(gè)變量的指針和捕獲的變量%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 }>*開辟一塊堆區(qū)內(nèi)控空間并轉(zhuǎn)換成{ %swift.refcounted, %swift.function }結(jié)構(gòu)體%13 = getelementptr inbounds <{ %swift.refcounted, %swift.function }>, <{ %swift.refcounted, %swift.function }>* %12, i32 0, i32 1取出{ %swift.refcounted, %swift.function }結(jié)構(gòu)體的第1個(gè)元素%swift.function%.fn = getelementptr inbounds %swift.function, %swift.function* %13, i32 0, i32 0取出%swift.function結(jié)構(gòu)體的第0個(gè)元素也就是void*函數(shù)地址store i8* %8, i8** %.fn, align 8將函數(shù)f的地址存到上面的函數(shù)地址中%.data = getelementptr inbounds %swift.function, %swift.function* %13, i32 0, i32 1
store %swift.refcounted* %9, %swift.refcounted** %.data, align 8取出%swift.function結(jié)構(gòu)體的第1個(gè)元素將捕獲的變量存入
下面又進(jìn)行了一系列的操作,這里其實(shí)不難發(fā)現(xiàn)在這個(gè)過程當(dāng)中,對閉包又重新進(jìn)行了一層包裝。閉包的結(jié)構(gòu)體是{ i8*, %swift.refcounted* }包裝成了{ %swift.refcounted, %swift.function }也就是{{ i64*, i64 } , { i8*, %swift.refcounted* }}根據(jù)之前文章中對閉包的還原可以得到如下結(jié)構(gòu)
// 中間層
struct ReabstractionThunkContext<Context> {
var heapObject: HeapObject
var function: ClosureData<Context>
}
struct HeapObject {
var matedata: UnsafeRawPointer
var refcount1: Int32
var refcount2: Int32
}
struct ClosureData<T>{
var ptr: UnsafeRawPointer
var object: UnsafePointer<T>
}
struct Box<T>{
var object: HeapObject
var value: T
}
由此可以得出: 當(dāng)給一個(gè)泛型參數(shù)傳入一個(gè)函數(shù)時(shí),這個(gè)時(shí)候泛型 T 為函數(shù),此時(shí)它會(huì)通過重新抽象的中間層里取到函數(shù)的地址來進(jìn)行執(zhí)行。所以本質(zhì)上,當(dāng)把閉包或者函數(shù)當(dāng)作泛型參數(shù)進(jìn)行傳值的時(shí)候,它為了使泛型的管理統(tǒng)一,也是重新抽象了一層中間層來捕獲當(dāng)前傳進(jìn)來的函數(shù)。