Swift進(jìn)階-泛型

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)類型,就像上邊例子中 StackProtocolItem 類型一樣。
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ù), stack1stack2 。 stack1 形式參數(shù)是 T1 類型, stack2 形式參數(shù)是 T2 類型。 T1T2 是兩個(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ì)于 VIPUSer 來說他們的行為是一致的,所以這里我們希望抽象出一個(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)行分析:

main
testGenric

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

ValueWitnessTable結(jié)構(gòu)體

截圖是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打印的是什么?

image.png

得到函數(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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容