swift-- 泛型
2020-12-29 本文已影响0人
Mjs
类型约束
在⼀个类型参数后⾯放置协议或者是类,⽐如就要求我们的类型参数 T
遵循 Equatable
协议
func test<T: Equatable>(_ a: T ,_ b: T) -> Bool{
return a == b
}
关联类型
在定义协议的时候,使⽤关联类型给协议中⽤到的类型起⼀个占位符名称。
protocol StackProtocol {
associatedtype Item
}
struct Stack: StackProtocol{
typealias Item = Int
private var items = [Item]()
mutating func push(_ item: Item){
items.append(item)
}
Where语句
添加限制条件
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]
}
}
func compare<T1: StackProtocol, T2: StackProtocol>(_ stack1: T1, _ stack2: T2)-> Bool where T1.Item == T2.Item, T1.Item: Equatable {
guard stack1.itemCount == stack2.itemCount else {
return false
}
for i in 0..<stack1.itemCount {
if stack1.index(of: i) != stack2.index(of: i) {
return false
}
}
return true
}
有时候我们希望在当前泛型制定类型的时候拥有特定功能
extension StackProtocol where Item == Int{
func test(){
print("test")
}
}
泛型函数
class Teacher{}
func testGen<T>(_ value: T)->T{
let tmp = value
return value
}
testGen(10)
testGen((10,20))
testGen(Teacher())
查看IR
define hidden swiftcc void @"$s4main7testGenyxxlF"(%swift.opaque* noalias nocapture sret, %swift.opaque* noalias nocapture, %swift.type* %T) #0 {
entry:
%T1 = alloca %swift.type*, align 8
%tmp.debug = alloca i8*, align 8
%2 = bitcast i8** %tmp.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
store %swift.type* %T, %swift.type** %T1, align 8
%3 = bitcast %swift.type* %T to i8***
%4 = getelementptr inbounds i8**, i8*** %3, i64 -1
//从当前Metadata中拿到valueWitnesses,index = 0 是Kind
%T.valueWitnesses = load i8**, i8*** %4, align 8, !invariant.load !42, !dereferenceable !43
//做一个类型的转换
%5 = bitcast i8** %T.valueWitnesses to %swift.vwtable*
//在valueWitnesses中拿到当前的这个类型的size大小
%6 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %5, i32 0, i32 8
%size = load i64, i64* %6, align 8, !invariant.load !42
//根据拿到的size大小来分配内存空间
%7 = alloca i8, i64 %size, align 16
call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7)
%8 = bitcast i8* %7 to %swift.opaque*
//初始化tmp的内存空间
store i8* %7, i8** %tmp.debug, align 8
%9 = getelementptr inbounds i8*, i8** %T.valueWitnesses, i32 2
%10 = load i8*, i8** %9, align 8, !invariant.load !42
//copy
%initializeWithCopy = bitcast i8* %10 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)*
%11 = call %swift.opaque* %initializeWithCopy(%swift.opaque* noalias %8, %swift.opaque* noalias %1, %swift.type* %T) #5
%12 = call %swift.opaque* %initializeWithCopy(%swift.opaque* noalias %0, %swift.opaque* noalias %1, %swift.type* %T) #5
%13 = getelementptr inbounds i8*, i8** %T.valueWitnesses, i32 1
%14 = load i8*, i8** %13, align 8, !invariant.load !42
//destroy
%destroy = bitcast i8* %14 to void (%swift.opaque*, %swift.type*)*
call void %destroy(%swift.opaque* noalias %8, %swift.type* %T) #5
%15 = bitcast %swift.opaque* %8 to i8*
call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15)
ret void
}
valueWitnesses
: 值目击表
泛型类型使⽤ VWT 进⾏内存管理,VWT 由编译器⽣成,其存储了该类型的 size、aligment(对⻬⽅ 式)以及针对该类型的基本内存操作。
当对泛型类型进⾏内存操作(如:内存拷⻉)时,最终会调⽤对应泛型类型的 VWT 中的基本内存操作。 泛型类型不同,其对应的 VWT 也不同。下图所示为⼀个⼩的值类型和⼀个引⽤类型的 VWT。
总结:
- 对于⼀个值类型,如:integer。该类型的 copy 和 move 操作会进⾏内存拷⻉;destroy 操作则不进 ⾏任何操作。
- 对于⼀个引⽤类型,如:class。该类型的 copy 操作会对引⽤计数加 1;move 操作会拷⻉指针,⽽不 会更新引⽤计数;destroy 操作会对引⽤计数减 1
泛型的⽅法调⽤
func makeIncrement() -> (Int) -> Int{
var runningTotal = 10
return {
runningTotal += $0
return runningTotal
}
}
func testGen<T>(_ value: T){
}
//{i8 * , swift type *}
let m = makeIncrement()
testGen(m)
查看IR
define i32 @main(i32, i8**) #0 {
entry:
%2 = alloca %swift.function, align 8
%3 = bitcast i8** %1 to i8*
%4 = call swiftcc { i8*, %swift.refcounted* } @"$s4main13makeIncrementS2icyF"()
%5 = extractvalue { i8*, %swift.refcounted* } %4, 0//闭包表达式的地址
%6 = extractvalue { i8*, %swift.refcounted* } %4, 1//捕获值的地址
//把值存放f这个变量中,并转换成指针
store i8* %5, i8** getelementptr inbounds (%swift.function, %swift.function* @"$s4main1myS2icvp", i32 0, i32 0), align 8
store %swift.refcounted* %6, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main1myS2icvp", 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* @"$s4main1myS2icvp", i32 0, i32 0), align 8
%9 = load %swift.refcounted*, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main1myS2icvp", i32 0, i32 1), align 8
%10 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %9) #2
//创建一个结构体
%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) #2
%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 (%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*
%15 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$sS2icMD") #4
call swiftcc void @"$s4main7testGenyyxlF"(%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) #2
%17 = bitcast %swift.function* %2 to i8*
call void @llvm.lifetime.end.p0i8(i64 16, i8* %17)
ret i32 0
}
struct FuntionData<T>{
var ptr: UnsafeRawPointer //
var captureValue: UnsafePointer<T>?
}
struct Box<T> {
var refCounted: HeapObject
var value: T
}
struct GenData<T>{
var ref: HeapObject
var function: FuntionData<T>
}
func makeIncrement() -> (Int) -> Int{
var runningTotal = 10
return {
runningTotal += $0
return runningTotal
}
}
func testGen<T>(_ value: T){
let ptr = UnsafeMutablePointer<T>.allocate(capacity: 1)
ptr.initialize(to: value)
let ctx = ptr.withMemoryRebound(to: FuntionData<GenData<Box<Int>>>.self, capacity: 1) {
$0.pointee.captureValue?.pointee.function.captureValue!
}
print(ctx?.pointee.value)
}
·············
Optional(10)