闭包尽量选择非逃逸闭包
函数是引用类型
func addTwoInts(_ a: Double, _ b: Double) -> Double {
return a + b
}
var a = addTwoInts
var b = a
函数的内存结构
1. 函数类型 type(of: a)
2.unsafeBitCast 指针转换
struct TargetFunctionTypeMetadata{
var kind: Int
var flags: Int
func numberArguments() -> Int{
return self.flags & 0x0000FFFF
}
}
let type = type(of: a)
let functionType = unsafeBitCast(type as Any.Type, to: UnsafeMutablePointer<TargetFunctionTypeMetadata>.self) print(functionType.pointee.numberArguments()) // 2
闭包
闭包的本质
{ i8*, %swift.refcounted* }
闭包的执行地址 + 捕获变量堆空间的地址
1. Box<T> 代表一个实例对象
2. HeapObject实例对象的metadata 和引用计数
3.Box<T> 中的value 捕获的值
struct ClosureData{
var ptr: UnsafeRawPointer // 闭包的执行地址
var capatureValue: UnsafePointer<Box<T>> // 捕获变量堆空间的地址
}
struct HeapObject{
var metadata: UnsafeRawPointer
var refcount1: Int32
var refcount2: Int32
}
struct Box<T>{
var object: HeapObject
var value: T
}
1. NoMeanStruct的内存地址和结构体第一个属性的内存地址是一样的,所以结构体NoMeanStruct的地址就是闭包的地址
struct NoMeanStruct{
var f: () -> Int
}
var a = 10
var f = NoMeanStruct {
a+= 30
return a
}
var ptr = UnsafeMutablePointer<NoMeanStruct>.allocate(capacity: 1)
ptr.initialize(to: f)
// ptr[0] = f
defer {
ptr.deinitialize(count: 1)
ptr.deallocate()
}
var ctx = ptr.withMemoryRebound(to: ClosureData<Box<Int>>.self, capacity: 1) {
return $0.pointee
}
print(ctx)
1. 第一个打印出结果, 闭包的本质是闭包的执行地址 + 捕获变量堆空间的地址
2.第二个 lldb,看出ptr 是闭包的执行地址
3.第三个lldb,看出object是个堆地址
.ll 文件简单语法
<type> *指针类型
[<elementnumber> x <elementtype> ] 数组
{..., ...} 结构体
局部变量 var runningTotal = 10
func makeIncrementer() -> () -> Int {
var runningTotal = 10 //局部变量
func incrementer() -> Int {
runningTotal+= 1
return runningTotal
}
return incrementer
}
let incr = makeIncrementer()
解析成.ll 文件
define i32 @main(i32 %0, i8** %1) #0 {
entry:
...
%3 = call swiftcc { i8*, %swift.refcounted* } @"$s4main15makeIncrementerSiycyF"()
...
}
makeIncrementer() 返回 { i8*, %swift.refcounted* } 一个结构体,里面2个指针
%swift.refcounted = type { %swift.type*, i64 }
%swift.type = type { i64 }
推断出闭包是一个{ i8*, type { type { i64 }, i64 }}
逃逸闭包
延长闭包的生命周期
逃逸闭包在函数内部异步执行或者被属性存储
非逃逸闭包
不会产生循环引用,函数作用域内释放
编译器更多性能优化 (retain, relsase)
上下文的内存保存再栈上,不是堆上