Swift探索( 九): 泛型

一: 泛型

1.1 什么是泛型

泛型可以将类型参数化,提高代码复用效率,减少代码量。

1.2 泛型解决的问题

下面是一个标准的非泛型函数 swapTwoInts(_:_:),用来交换两个 Int 值:

func swapTwoInts(_ a: inout Int, _ b: inout Int)  {
    let tempA = a
    a = b
    b = tempA
}

这个函数使用输入输出参数(inout)来交换 ab 的值。 swapTwoInts(_:_:) 函数将 b 的原始值换成了 a,将 a 的原始值换成了 b,可以调用这个函数来交换两个 Int 类型变量

var someInt =  3
var anotherInt =  107
swapTwoInts(&someInt,  &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")

打印结果
someInt is now 107, and anotherInt is now 3

swapTwoInts(_:_:) 函数很实用,但它只能作用于 Int 类型。如果想交换两个 String 类型值,或者 Double 类型值,你必须编写对应的函数,类似下面 swapTwoStrings(_:_:)swapTwoDoubles(_:_:) 函数:

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(_:_:) 函数体是一样的,唯一的区别是它们接受的参数类型(IntStringDouble)。

在实际应用中,通常需要一个更实用更灵活的函数来交换两个任意类型的值,幸运的是,泛型代码就是为了解决这种问题而存在。

1.3 泛型基本语法

1.3.1 泛型函数

首先要指定一个占位符 T ,紧挨着写在函数名后面的一对尖括号,其次我们就可以使用 T 来替换任意定义的函数形式参数

func swapTwoValues<T>(_ a: inout T, _ b: inout T)  {
    let tempA = a
    a = b
    b = tempA
}

泛型版本的函数使用 占位符 类型名(这里叫做 T ),而不是 实际类型名(例如 IntStringDouble ),占位符类型名并不关心 T 具体的类型,但它要求 ab 必须是相同的类型,T 的实际类型由每次调用 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)")

打印结果
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()
    }
}

这个结构体在栈中使用一个名为 items 的数组属性来存储值。栈提供了两个方法:push(_:)pop() ,用来向栈中压入值以及从栈中移除值。这些方法被标记为 mutating ,因为它们需要修改结构体的 items 数组。
上面的 IntStack 结构体只能用于 Int 类型。不过,可以定义一个泛型 Stack 结构体,从而能够处理任意类型的值。

struct Stack<Element> {
    var items: [Element] = []
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

注意,Stack 基本上和 IntStack 相同,只是用占位类型参数 Element 代替了实际的 Int 类型。这个类型参数包裹在紧随结构体名的一对尖括号里( <Element> )。
Element 为待提供的类型定义了一个占位名。这种待提供的类型可以在结构体的定义中通过 Element 来引用。
在这个例子中,Element 在如下三个地方被用作占位符:

  • 创建 items 属性,使用 Element 类型的空数组对其进行初始化。
  • 指定 push(_:) 方法的唯一参数 item 的类型必须是 Element 类型。
  • 指定 pop() 方法的返回值类型必须是 Element 类型。
    由于 Stack 是泛型类型,因此可以用来创建适用于 Swift 中任意有效类型的栈,就像 ArrayDictionary 那样。
    可以通过在尖括号中写出栈中需要存储的数据类型来创建并初始化一个 Stack 实例。例如,要创建一个 String 类型的栈,可以写成 Stack<String>():
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
1.3.3 关联类型

对于协议来说是无法像函数、类型那样直接在协议名后添加 <T> 那样使用泛型,需要我们使用关联类型来代替。关联类型通过 associatedtype 关键字来指定。

protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

关联类型为协议中的某个类型提供了一个占位符名称,其代表的实际类型在协议被遵循时才会被指定。

struct IntStack: Container {
    // IntStack 的原始实现部分
    var items: [Int] = []
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
    // Container 协议的实现部分
    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 添加特定的类型约束,这将在某些情况下非常有用。类型约束指定类型参数必须继承自指定类、遵循特定的协议或协议组合。

protocol myProtocol {
    func protocolFunc()
}

func test<T: myProtocol> (_ value1: T, _ value2: T) {
    value1.protocolFunc()
    value2.protocolFunc()
}

也可以在协议里给关联类型添加约束来要求遵循的类型满足约束

protocol Container {
    associatedtype Item: Equatable
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

要遵守 Container 协议,Item 类型也必须遵守 Equatable 协议。
协议可以作为它自身的要求出现。例如,有一个协议细化了 Container 协议,添加了一个 suffix(_:) 方法。

protocol SuffixContainer: Container {
    associatedtype Suffix: SuffixContainer where Suffix.Item == Item
    func suffix(_ size: Int) -> Suffix
}

在这个协议里,Suffix 是一个关联类型,就像上边例子中 ContainerItem 类型一样。Suffix 拥有两个约束:它必须遵循 SuffixContainer 协议(就是当前定义的协议),以及它的 Item 类型必须是和容器里的 Item 类型相同。Item 的约束是一个 where 分句。
泛型 Where 分句要求了关联类型必须遵循指定的协议,或者指定的类型形式参数和关联类型必须相同。泛型 Where 分句以 Where 关键字开头,后接关联类型的约束或类型和关联类型一致的关系。

二:类型擦除

类型擦除 是一种非常有用的技术,是将具体类型的类型信息擦除掉,只将类型的抽象信息,通常指的是类型尊从的协议、接口、或基类暴露出来。我们通过下面这个案例进一步了解什么是类型擦除。

// 定义一个协议: 用于数据提取
protocol DataFetch {
    associatedtype dataType
    // 数据提取方法
    func fetchData(completion: ((Result<dataType, Error>) -> Void)?)
}

这里有一个协议 DataFetch,协议里有一个关联类型 dataType,这个协议用来提取数据用的,它声明里一个提取数据的方法 fetchData(completion:) 这里传入的是一个闭包 (Result<dataType, Error>) -> Void) 接收一个 Result 类型的参数,并且第一个参数就是泛型协议的关联类型 dataType ,因为我们不知道提取出来的数据是字典类型、字符串类型还是数组类型或者是其它类型。

struct UserModel {
    let uId: Int
    let userType: String
}

// 定义一个UserData  准守泛型协议 DataFetch 并且实现了其中的方法 fetchData 返回一个 User的数据模型
struct UserData: DataFetch {
    
    typealias dataType = UserModel
    
    func fetchData(completion: ((Result<UserModel, Error>) -> Void)?) {
        let user = UserModel(uId: 10001, userType: "普通会员")
        completion?(.success(user))
    }
}

// 定义一个VipUserData  准守泛型协议 DataFetch 并且实现了其中的方法 fetchData 返回一个 User的数据模型
struct VipUserData: DataFetch {
    
    typealias dataType = UserModel
    
    func fetchData(completion: ((Result<UserModel, Error>) -> Void)?) {
        let user = UserModel(uId: 10001, userType: "高级会员")
        completion?(.success(user))
    }
}

这里我定义了两个类型 UserDataVipUserData 都准守了 DataFetch 协议并都实现了协议中的方法 fetchData(completion:) 但是返回的 UserModel 中的内容不一致,一个是普通会员,一个是高级会员。

当一个类中包含了一个遵守 DataFetch 协议类型的变量,但这个变量的类型并不是单一的,而希望它支持遵守了 DataFetch 协议的其它类型,因为我们不需要关心数据是如何提取的,只关心提取数据后的结构。此时我们把这个变量当作该类的一个属性或者一个方法中的参数,第一时间想到的是用 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)
            }
        }
    }
}

此时编译器去报错了

DataFetch.png

这里报错是因为:协议 DataFetch 只能用作泛型约束,不能用作具体类型。因为编译器无法确定关联类型 dataType 的具体类型是什么。如果直接将 userData 的类型改成 UserData ,从某些角度来看是这么做是可以的,但是这会在 VCUserData 对象之间创建一个依赖关系。如果我们遵循 SOLID 原则(简单地说:接口职责应该单一,不要承担过多的职责。),我们希望避免依赖并隐藏实现细节。因此这里使用 类型擦除 技术。我们就需要引入一个中间层

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)
    }
}
  • 这里我们定义了一个中间层结构体 AnyDataFetchAnyDataFetch 实现了 DataFetch 的所有方法。
  • AnyDataFetch 的初始化过程中,实现协议的类型会被当做参数传入(依赖注入)
  • AnyDataFetch 实现的具体协议方法 fetchData 中,再转发实现协议的抽象类型。
    这个时候我们就可以把 AnyDataFetch 当做具体类型使用。
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()


打印结果:

普通会员
-----------
高级会员

这样做的好处就是对与 homeVC 来说不用知道当前请求的具体类型是什么( 可以是 UserData 也可以是 VipUserData ) , homeVC 接收的其实就只是 AnyDataFetch<UserModel> 类型,这其实就是所谓的 类型擦除 。当有另一个协议的抽象类型 ( superVipUserData ) 的时候,我们不需要改变 homeVC 的代码,不需要改变 AnyDataFetch 的代码。
系统中的 AnySequence , AnyCollection 都是这样的原理。

三: 泛型的内存结构

3.1: 泛型内存结构分析

Swift探索(七): 闭包 中我们还原了函数的内存结构,那么在今天这篇文章中加上泛型的函数的内存结构又是什么样的呢?

func test <T>(_ value: T) -> T{
    let temp = value;
    return temp
}

test(10)

通过 swiftc main.swift -emit-ir > ./main.ll 编译成 IR 文件,并且定位到 test() 函数的调用

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 就是传入进来的泛型 T 的类型,也就是在调用时,是什么类型这里就是什么类型。
  • %T1 = alloca %swift.type*, align 8 之前的文章当中提到过 swift.type 类型,其实就是 heapObject 结构体。
  • store %swift.type* %T, %swift.type** %T1, align 8T 存储到 T1 中,这也就说明了不管是分配内存空间还是管理这个值的内存,都是依赖于当前的类型的 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* 转成成 %swift.vwtable 结构体
    剩下的代码就是处理 %swift.vwtable 里的东西。
    通过 IR 代码我们不难看出泛型函数中的泛型是通过 %swift.vwtable 来进行管理内存的。

3.2 ValueWitnessTable 值见证表

IR 代码的最上面我们可以看到 %swift.vwtable 的结构如下

%swift.vwtable = type { i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i64, i64, i32, i32 }

这里的 i8* 可以把它当作 void*,也就是这些 i8* 其实就是当前所谓的函数,根据这个结构体可以还原出 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
}

综上可知泛型函数中的泛型不管是 值类型 还是 引用类型 他的内存结构中都是有 ValueWitnessTable , 并且是在 metadata 的前面,ValueWitnessTable 保存着这个类型的 sizestrideflagsextraInhabitantCount 还有一些 内存管理函数 等信息。

3.3 函数(闭包)作为泛型参数

如果是函数或者闭包做为参数传入到泛型函数里,又会不会有什么不一样的吗?

func makeIncrementer() -> () -> Void {
    var runningTotal = 10
    func incrementer() {
        runningTotal += 10
    }
    return incrementer
}

func test <T>(_ value: T){
    
}

let f = makeIncrementer()

test(f)

同样编译成 IR 代码定位到 main 函数的调用

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探索(七): 闭包 探究过 就是创建当前的闭包表达式

  • 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* 这三句就是存储指针和捕获的变量到 f里并将 f 转换成 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 这个变量的指针和捕获的变量

  • %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 }>* 开辟一块堆区内控空间并转换成 { %swift.refcounted, %swift.function } 结构体

  • %13 = getelementptr inbounds <{ %swift.refcounted, %swift.function }>, <{ %swift.refcounted, %swift.function }>* %12, i32 0, i32 1 取出 { %swift.refcounted, %swift.function } 结构体的第 1 个元素 %swift.function

  • %.fn = getelementptr inbounds %swift.function, %swift.function* %13, i32 0, i32 0 取出 %swift.function 结构体的第 0 个元素也就是 void* 函数地址

  • store i8* %8, i8** %.fn, align 8 将函数 f 的地址存到上面的函数地址中

  • %.data = getelementptr inbounds %swift.function, %swift.function* %13, i32 0, i32 1
    store %swift.refcounted* %9, %swift.refcounted** %.data, align 8 取出 %swift.function 结构体的第 1 个元素将捕获的变量存入
    下面又进行了一系列的操作,这里其实不难发现在这个过程当中,对闭包又重新进行了一层包装。闭包的结构体是 { i8*, %swift.refcounted* } 包装成了 { %swift.refcounted, %swift.function } 也就是 {{ i64*, i64 } , { i8*, %swift.refcounted* }} 根据之前文章中对闭包的还原可以得到如下结构

// 中间层
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
}

由此可以得出: 当给一个泛型参数传入一个函数时,这个时候泛型 T 为函数,此时它会通过重新抽象的中间层里取到函数的地址来进行执行。所以本质上,当把闭包或者函数当作泛型参数进行传值的时候,它为了使泛型的管理统一,也是重新抽象了一层中间层来捕获当前传进来的函数。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容

  • 一:协议 1.1 协议的定义 协议可以用来定义 方法、属性 、下标的声明 ,协议可以被 枚举、结构体、类遵守(多个...
    Lee_Toto阅读 511评论 0 1
  • 闭包是可以在你的代码中被传递和引用的功能性独立代码块。闭包在实现上是一个结构体,它存储了一个函数(通常是其入口地址...
    HotPotCat阅读 1,022评论 1 4
  • 协议方法的调度 protocol Incrementable{ var age: Int {set get} ...
    张天宇_bba7阅读 202评论 0 2
  • 一:函数类型 每个函数都有种特定的函数类型,函数的类型由函数的参数类型和返回类型组成。 上述代码中 (Double...
    Lee_Toto阅读 578评论 0 1
  • Swift — 泛型(Generics) [TOC] 本文将介绍泛型的一些用法、关联类型、where语句,以及对泛...
    just东东阅读 1,311评论 0 3