Swift进阶-闭包
Swift进阶-类与结构体
Swift-函数派发
Swift进阶-属性
Swift进阶-指针
Swift进阶-内存管理
Swift进阶-TargetClassMetadata和TargetStructMetadata数据结构源码分析
Swift进阶-Mirror解析
Swift进阶-闭包
Swift进阶-协议
Swift进阶-泛型
Swift进阶-String源码解析
Swift进阶-Array源码解析
一、函数类型
函数本身也有自己的类型,它由 形参类型和返回类型 组成。
func swift_add(_ a: Double, _ b: Double) -> Double {
return a + b
}
//引用类型
var a: (Double, Double) -> Double = swift_add
print(a(10, 20))
var b = a
print(b(20 ,30))
断点调试
上面调试结果可以看出来:
- 函数类型是引用类型;
- a指向的地址第一个8字节存储的是函数类型。
函数类型是引用类型的案例:
func makeIncrementer() -> () -> Int {
var runningTotal = 10
func incrementer() -> Int {
runningTotal += 1
return runningTotal
}
return incrementer
}
let increm = makeIncrementer() // 引用类型去理解
print(increm()) // 11
print(increm()) // 12
print(increm()) // 13
// 理解成每次都在创建内部函数对象
print(makeIncrementer()()) // 11
print(makeIncrementer()()) // 11
print(makeIncrementer()()) // 11
函数类型的元数据TargetFunctionTypeMetadata
的源码声明:
/// The structure of function type metadata.
template <typename Runtime>
struct TargetFunctionTypeMetadata : public TargetMetadata<Runtime> {
using StoredSize = typename Runtime::StoredSize;
using Parameter = ConstTargetMetadataPointer<Runtime, swift::TargetMetadata>;
TargetFunctionTypeFlags<StoredSize> Flags;
/// The type metadata for the result type.
ConstTargetMetadataPointer<Runtime, swift::TargetMetadata> ResultType;
Parameter *getParameters() { return reinterpret_cast<Parameter *>(this + 1); }
const Parameter *getParameters() const {
return reinterpret_cast<const Parameter *>(this + 1);
}
Parameter getParameter(unsigned index) const {
assert(index < getNumParameters());
return getParameters()[index];
}
ParameterFlags getParameterFlags(unsigned index) const {
assert(index < getNumParameters());
auto flags = hasParameterFlags() ? getParameterFlags()[index] : 0;
return ParameterFlags::fromIntValue(flags);
}
StoredSize getNumParameters() const {
return Flags.getNumParameters();
}
FunctionMetadataConvention getConvention() const {
return Flags.getConvention();
}
bool isAsync() const { return Flags.isAsync(); }
bool isThrowing() const { return Flags.isThrowing(); }
bool isSendable() const { return Flags.isSendable(); }
bool isDifferentiable() const { return Flags.isDifferentiable(); }
bool hasParameterFlags() const { return Flags.hasParameterFlags(); }
bool isEscaping() const { return Flags.isEscaping(); }
bool hasGlobalActor() const { return Flags.hasGlobalActor(); }
static constexpr StoredSize OffsetToFlags = sizeof(TargetMetadata<Runtime>);
static bool classof(const TargetMetadata<Runtime> *metadata) {
return metadata->getKind() == MetadataKind::Function;
}
uint32_t *getParameterFlags() {
return reinterpret_cast<uint32_t *>(getParameters() + getNumParameters());
}
const uint32_t *getParameterFlags() const {
return reinterpret_cast<const uint32_t *>(getParameters() +
getNumParameters());
}
TargetFunctionMetadataDifferentiabilityKind<StoredSize> *
getDifferentiabilityKindAddress() {
assert(isDifferentiable());
void *previousEndAddr = hasParameterFlags()
? reinterpret_cast<void *>(getParameterFlags() + getNumParameters())
: reinterpret_cast<void *>(getParameters() + getNumParameters());
return reinterpret_cast<
TargetFunctionMetadataDifferentiabilityKind<StoredSize> *>(
llvm::alignAddr(previousEndAddr,
llvm::Align(alignof(typename Runtime::StoredPointer))));
}
TargetFunctionMetadataDifferentiabilityKind<StoredSize>
getDifferentiabilityKind() const {
if (isDifferentiable()) {
return *const_cast<TargetFunctionTypeMetadata<Runtime> *>(this)
->getDifferentiabilityKindAddress();
}
return TargetFunctionMetadataDifferentiabilityKind<StoredSize>
::NonDifferentiable;
}
ConstTargetMetadataPointer<Runtime, swift::TargetMetadata> *
getGlobalActorAddr() {
assert(hasGlobalActor());
void *endAddr =
isDifferentiable()
? reinterpret_cast<void *>(getDifferentiabilityKindAddress() + 1) :
hasParameterFlags()
? reinterpret_cast<void *>(getParameterFlags() + getNumParameters()) :
reinterpret_cast<void *>(getParameters() + getNumParameters());
return reinterpret_cast<
ConstTargetMetadataPointer<Runtime, swift::TargetMetadata> *>(
llvm::alignAddr(
endAddr, llvm::Align(alignof(typename Runtime::StoredPointer))));
}
ConstTargetMetadataPointer<Runtime, swift::TargetMetadata>
getGlobalActor() const {
if (!hasGlobalActor())
return ConstTargetMetadataPointer<Runtime, swift::TargetMetadata>();
return *const_cast<TargetFunctionTypeMetadata<Runtime> *>(this)
->getGlobalActorAddr();
}
};
using FunctionTypeMetadata = TargetFunctionTypeMetadata<InProcess>;
-
TargetFunctionTypeMetadata
继承自TargetMetadata
说明它拥有Kind
属性(也就是isa); -
TargetFunctionTypeFlags<StoredSize> Flags;
作用是标识了函数的类型;
-
ConstTargetMetadataPointer<Runtime, swift::TargetMetadata> ResultType;
是返回值类型
4.其实TargetFunctionTypeMetadata
还拥有一个参数列表,它其实是一个连续的内存空间
于是乎可以分析出TargetFunctionTypeMetadata
的数据结构:
struct TargetFunctionTypeMetadata{
var kind: Int // isa
var flags: Int
var resultType: UnsafeRawPointer // 返回值类型
var arguments: ArgumentsBuffer<Any.Type> // 参数类型列表
// 获取参数个数
func numberArguments() -> Int {
return self.flags & 0x0000FFFF
}
}
struct ArgumentsBuffer<Element>{
var element: Element
mutating func buffer(n: Int) -> UnsafeBufferPointer<Element> {
return withUnsafePointer(to: &self) {
let ptr = $0.withMemoryRebound(to: Element.self, capacity: 1) { start in
return start
}
return UnsafeBufferPointer(start: ptr, count: n)
}
}
mutating func index(of i: Int) -> UnsafeMutablePointer<Element> {
return withUnsafePointer(to: &self) {
return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self).advanced(by: i))
}
}
}
于是乎验证一下推测来的数据结构对不对:
let functionType = type(of: swift_add)
let functionPointer = unsafeBitCast(functionType as Any.Type, to: UnsafeMutablePointer<TargetFunctionTypeMetadata>.self)
print(functionPointer.pointee.numberArguments()) // 2
print(functionPointer.pointee.arguments.index(of: 0).pointee) // Double
let resultType = unsafeBitCast(functionPointer.pointee.resultType, to: Any.Type.self)
print(resultType) // Double
二、闭包
1.闭包概念
闭包是一个捕获了上下文的常量/变量的函数。
可以看一个官方给的案例:
func makeIncrementer() -> () -> Int {
var runningTotal = 10
func incrementer() -> Int {
runningTotal += 1
return runningTotal
}
return incrementer
}
var a = makeIncrementer()
这里incrementer
作为一个闭包,显然他是一个函数,其次为了保证其执行,要捕获外部变量runningTotal
到内部,所以闭包的关键就有捕获外部变量或常量
和函数
。
2.闭包表达式
闭包在语法上有这样的标准结构: {(参数列表) -> 返回值 in 闭包体}
{ (param) -> (returnType) in
//do somethings
}
- 作用域(也就是大括号)
- 参数和返回值
- 函数体in之后的代码
2.1 闭包即可以当做变量
// 也可以用可选性修饰 var closure: (String)->String = {...}
var closure = { (name: String) in
return name
}
print(closure("安老师"))
2.2 闭包即可以当做函数的参数
// 如果用let修饰closure,那一旦赋值就不可改变
var closure = { (name: String) in
return name
}
func call(_ closure: (String)->String) -> String {
let name = closure("安老师")
return name
}
print(call(closure))
2.3 闭包即可以当做函数的返回值
func makeIncrementer() -> () -> Int {
var runningTotal = 10
func incrementer() -> Int {
runningTotal += 1
return runningTotal
}
return incrementer
}
print(makeIncrementer()())
3.尾随闭包
当我们把闭包表达式作为函数的最后一个参数,如果当前的闭包表达式很长,我们可以通过尾随 闭包的书写方式来提高代码的可读性。
func test(_ a: Int, _ b: Int, _ c: Int, by: (_ item1: Int, _ item2: Int, _ item3: Int) -> Bool) -> Bool{
return by(a, b, c)
}
test(10, 20, 30, by: {(_ item1: Int, _ item2: Int, _ item3: Int) -> Bool in
return (item1 + item2 < item3)
})
闭包表达式的好处 有很多:
- 利用上下文推断参数和返回值类型
- 单表达式可以隐士返回,既省略 return 关键字
- 参数名称的简写(比如我们的 $0)
- 尾随闭包表达式
var array = [1, 2, 3]
array.sort(by: {(item1 : Int, item2: Int) -> Bool in return item1 < item2 })
array.sort(by: {(item1, item2) -> Bool in return item1 < item2 })
array.sort(by: {(item1, item2) in return item1 < item2 })
array.sort{(item1, item2) in item1 < item2 }
array.sort{ return $0 < $1 }
array.sort{ $0 < $1 }
array.sort(by: <)
4.捕获值特性
-
Objective-C
的Block
类型区分:全局、栈、堆
; -
swift
的闭包
都没有这种概念了,var closure = { print("closure") } 中的closure存储的是type metadata。
4.1 Objective-C
的 Block
类型
全局Block
:不捕获外部变量(只使用静态变量、全局变量);
栈Block
:捕获局部变量、OC属性;
堆Block
:捕获局部变量、OC属性,并赋值给强引用(或copy修饰的变量)。
栈block与堆block的案例:
- (void)testBlock{
NSObject *o = [NSObject new];
NSLog(@"%ld", CFGetRetainCount((__bridge CFTypeRef)o)); // 1
// 堆block (本身block在栈上,再赋值给了堆,所以o被引用2次)
void(^strongBlock)(void) = ^{
NSLog(@"%ld", CFGetRetainCount((__bridge CFTypeRef)o));
};
strongBlock(); // 3
// 栈block
void(^__weak weakBlock)(void) = ^{
NSLog(@"%ld", CFGetRetainCount((__bridge CFTypeRef)o));
};
weakBlock(); // 4
// 堆block (由栈block转化为堆block,o还被引用了1次,引用计数还得+1)
void(^copyBlock)(void) = [weakBlock copy];
copyBlock(); // 5
}
// 1 3 4 5
4.2 block
与闭包
在捕获外部变量时的特性区别
- (void)testBlock {
NSInteger i = 1;
void(^block) (void) = ^{
NSLog(@"block %ld:", i);
};
i += 1;
NSLog(@"before block %ld:", i); // 2
block(); //1
NSLog(@"after block %ld:", i); //2
}
如果想要外部被修改能够影响当前 Block 内部捕获的值,我们只需要对当前的 i 添加 __block 修饰符
:
- (void)testBlock {
__block NSInteger i = 1;
void(^block)(void) = ^{
NSLog(@"block %ld:", i);
};
i += 1;
NSLog(@"before block %ld:", i); // 2
block(); // 2
NSLog(@"after block %ld:", i); // 2
}
把上面的代码翻译成swift
代码来看一下它的不一样:
// swift是没有__block修饰的
var i = 1
let closure = {
print("closure \(i)")
}
i += 1
print("before closure \(i)") // 2
closure() // 2
print("after closure \(i)") // 2
区别:当闭包在使用 i 的时候,直接使用外部全局变量取 i 的值。看看sil
:
上面的案例举的全局变量案例,那如果把上面的代码放到函数体里呢?
func test(){
var i = 1
let closure = {
print("closure:\(i)")
}
i += 1
print("before closure:\(i)") // 2
closure() // 2
print("after closure:\(i)") // 2
}
可以发现,打印结果没什么变化。看完sil
再下结论:
project_box
与之对应的是alloc_box
;alloc_box
在捕获的时候会在堆区上开辟内存空间以存储变量 i 的值;project_box
是用到该值的时候,从堆上取出,再对其取值。
所以闭包在堆区开辟内存空间的时候,有Metadata、Refcount 和 i。
总结:
1.闭包是一个引用类型;
2.闭包捕获全局(静态)变量/常量默认是不捕获的,直接使用该变量/常量地址;
3.闭包捕获局部值类型变量会在堆区开辟内存空间;每次修改捕获变量的值的时候,其实是修改堆区当中的值;
4.闭包捕获局部引用类型变量,无需开辟堆区内存空间,直接可引用堆区地址;并且该变量引用计数+1,在闭包生命周期结束会把该变量引用计数-1。
5.闭包的本质
5.1 熟悉IR
语法:
数组
[<elementnumber> x <elementtype>] // 元素个数 x 元素类型
//example:
alloca [24 x i8], align 8 24个i8都是0
alloca [4 x i32] === array
结构体
%swift.refcounted = type { %swift.type*, i64 }
//example:
%T = type {<type list>} //这种和C语言的结构体类似
指针类型
<type> *
//example:
i64* //64位的整形
getelementptr 指令
struct munger_struct {
int f1;
int f2;
};
void munge(struct munger_struct *P) {
P[0].f1 = P[1].f1 + P[2].f2;
}
// 取出结构体首地址
getelementptr inbounds %struct.munger_struct, %struct.munger_struct %1, i64 0
// 1.取出结构体首地址,2.然后取出结构体第一个元素
getelementptr inbounds %struct.munger_struct, %struct.munger_struct %1, i32 0, i32 0
// 下面是一个案例:
int main(int argc, const char * argv[]) {
int array[4] = {1, 2, 3, 4};
int a = array[0];
return 0;
}
其中 int a = array[0] 这句对应的LLVM代码应该是这样的:
a = getelementptr inbounds [4 x i32], [4 x i32]* array, i64 0, i32 0, i32 0
总结:
第一个索引不会改变返回的指针的类型,也就是说ptrval前面的*对应什么类型,返回就是什 么类型
第一个索引的偏移量的是由第一个索引的值和第一个ty指定的基本类型共同确定的。
后面的索引是在数组或者结构体内进行索引
每增加一个索引,就会使得该索引使用的基本类型和返回的指针的类型去掉一层
5.2 闭包的数据结构
把下面代码编译成IR
,分析还原出闭包的数据结构
func makeIncrementer() -> () -> Int {
var runningTotal = 10
func incrementer() -> Int {
runningTotal += 1
return runningTotal
}
return incrementer
}
let increm = makeIncrementer()
找到main函数
打开终端输入,还原混写名称
$ xcrun swift-demangle s4main15makeIncrementerSiycyF
// ---------下面是输出结果---------------
$s4main15makeIncrementerSiycyF ---> main.makeIncrementer() -> () -> Swift.Int
swift.refcounted的IR声明
所以swift.refcounted是一个 {i64, i64}的结构体
所以对于我们调用makeIncrementer()
的返回值 { i8*, %swift.refcounted* }
就可以分析出闭包的最初始的数据结构
:
struct ClosureData {
var unkown: UnsafeRawPointer // i8*
var closureHeapObject: ClosureHeapObject // swift.refcounted
}
struct ClosureHeapObject {
var metadata: UnsafeRawPointer
var refcount1: Int32
var refcount2: Int32
}
接下来我们看看 s4main15makeIncrementerSiycyF
就是makeIncrementer函数到底是怎么构造出返回的东西 { i8*, %swift.refcounted* }
的:
define hidden swiftcc { i8*, %swift.refcounted* } @"$s4main15makeIncrementerSiycyF"() #0 {
entry:
%runningTotal.debug = alloca %TSi*, align 8
%0 = bitcast %TSi** %runningTotal.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
// %1堆空间的内存地址
%1 = call noalias %swift.refcounted* @swift_allocObject(%swift.type* getelementptr inbounds (%swift.full_boxmetadata, %swift.full_boxmetadata* @metadata, i32 0, i32 2), i64 24, i64 7) #1
// 做了一个指针类型的转换,相当于UnsafeBitcast
%2 = bitcast %swift.refcounted* %1 to <{ %swift.refcounted, [8 x i8] }>*
// 取出指向[8 x i8]的指针
%3 = getelementptr inbounds <{ %swift.refcounted, [8 x i8] }>, <{ %swift.refcounted, [8 x i8] }>* %2, i32 0, i32 1
// 把它转换成 %TSi* 类型 (%TSi* 其实是一个 type<{ i64 }> 64位的结构体)
%4 = bitcast [8 x i8]* %3 to %TSi*
// 存储
store %TSi* %4, %TSi** %runningTotal.debug, align 8
%._value = getelementptr inbounds %TSi, %TSi* %4, i32 0, i32 0
// 把10这个值放到%._value偏移8字节的内存空间里
store i64 10, i64* %._value, align 8
// 处理引用计数相关
%5 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #1
call void @swift_release(%swift.refcounted* %1) #1
// 将func里的内嵌闭包地址转换成 void * 插入到 { i8*, %swift.refcounted* } 的第一个元素
%6 = insertvalue { i8*, %swift.refcounted* } { i8* bitcast (i64 (%swift.refcounted*)* @"$s4main15makeIncrementerSiycyF11incrementerL_SiyFTA" to i8*), %swift.refcounted* undef }, %swift.refcounted* %1, 1
ret { i8*, %swift.refcounted* } %6
}
根据上面的IR
代码,还原出 ClosureData
的数据结构为:
struct ClosureData<Box> {
var ptr: UnsafeRawPointer // 闭包地址
var box: UnsafePointer<Box>
}
struct Box<T> {
var object: HeapObject // 实例对象的内存地址
var value: T
}
///实例对象的内存地址
struct HeapObject {
var metadata: UnsafeRawPointer
var refcount1: Int32
var refcount2: Int32
}
结论:ClosureData 的数据结构 = 闭包的执行地址 + 捕获变量堆空间的地址
下面是验证结论代码:
func makeIncrementer() -> ()->Int {
var runningTotal = 10
func incrementer() -> Int {
runningTotal += 1
return runningTotal
}
return incrementer
}
struct NoMeanStruct{
var closure: () -> Int
}
var closure = NoMeanStruct(closure: makeIncrementer())
let ptr = UnsafeMutablePointer<NoMeanStruct>.allocate(capacity: 1)
ptr.initialize(to: closure)
// 内存绑定
let closure_ptr = ptr.withMemoryRebound(to: ClosureData<Box<Int>>.self, capacity: 1){ $0.pointee }
print("闭包地址: \(closure_ptr.ptr)")
print("实例对象的内存地址: \(closure_ptr.box)")
print(closure_ptr.box.pointee.value)
print("end")
ptr.deinitialize(count: 3)
ptr.deallocate()
输出结果
输出的闭包地址0x0000000100005700
一定能在Mach-O
上面找到,所以可以使用下面的命令查找起来,可以证明输出的地址就是闭包地址:
// 注意地址在Mach-O上是不带0x的
$ nm -p 可执行文件的路径 | grep 0000000100005700
// 输出了一个混写的名称,把混写名称还原
$ xcrun swift-demangle 混写的名称
// 最后得到下面的内容
// $s11SwiftTest15makeIncrementerSiycyF11incrementerL_SiyFTA ---> partial apply forwarder for incrementer #1 () -> Swift.Int in SwiftTest.makeIncrementer() -> () -> Swift.Int
如果 makeIncrementer
有参数呢?那么Box的数据结构就变了:
func makeIncrementer(_ amount: Int) -> ()->Int {
var runningTotal = 10
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
struct Box<T1, T2> {
var object: HeapObject // 把value1、value2统一当成object的属性
var value1: UnsafePointer<T1>
var value2: T2
}
验证Box数据结构的改变
let funcStruct = NoMeanStruct(closure: makeIncrementer(20))
let func_ptr = UnsafeMutablePointer<NoMeanStruct>.allocate(capacity: 1)
func_ptr.initialize(to: funcStruct)
let closure_ptr = func_ptr.withMemoryRebound(to: ClosureData<Box<Int, Int>>.self, capacity: 1) { return $0.pointee }
print("闭包地址:\(closure_ptr.ptr)")
print("捕获的地址:\(closure_ptr.box.pointee.value1)")
print("参数的值:\(closure_ptr.box.pointee.value2)")
print("end")
func_ptr.deinitialize(count: 3)
func_ptr.deallocate()
三、C与Swift的函数调用
新建一个对C文件.h和.c
#ifndef TestC_h
#define TestC_h
#include <stdio.h>
int TesctCFUnction(int (callBack)(int a, int b));
#endif /* TestC_h */
#include "TestC.h"
int TesctCFUnction(int (callBack)(int a, int b)){
return callBack(10, 20);
}
从下图中就可以看出,该C文件声明的函数是否有被倒入到Swift:
还需要再桥接文件(ProjectName-Bridging-Header.h
)上倒入.h: #import "TestC.h"
;
在swift中就可以调用这个函数:
let result = TesctCFUnction { $0 + $1 }
print(result) // 30
但是,如果给定一个已经声明的闭包作为参数呢?它会报错!
image.png这个时候需要用到 @convention
关键字,意思是把swift闭包转换成c语言的回调函数
let closure: @convention(c)(Int32, Int32) -> Int32 = {(a: Int32, b: Int32) -> Int32 in
return a + b
}
let result = TesctCFUnction(closure)
print(result) // 30
四、逃逸闭包、非逃逸闭包与自动闭包
1.逃逸闭包
逃逸闭包的定义:
当闭包作为一个实际参数传递给一个函数的时候,并且是在函数返回之后调用,我们就说这个闭包逃逸了。当我们声明一个接受闭包作为形式参数的函数时,你可以在形式参数前写 @escaping
来明确闭包是允许逃逸的。
- 作为函数的参数传递
- 当前闭包在函数内部异步执行或者被存储
- 函数结束,闭包被调用,生命周期结束
情况一:
// 用属性去存储闭包
class Teacher {
var completion: ((Int)->Void)?
func makeIncrement(_ hander: @escaping (Int)->Void) {
self.completion = hander
}
func doSomething() {
makeIncrement() { result in
print(result)
}
}
}
let t = Teacher()
t.doSomething()
t.completion?(10)
情况二:
// 逃逸闭包二
class Teacher {
func makeIncrement(_ hander: @escaping (Int)->Void) {
DispatchQueue.global().asyncAfter(deadline: .now()+0.5) {
hander(10)
}
}
func doSomething() {
makeIncrement() { result in
print(result)
}
}
}
let t = Teacher()
t.doSomething()
情况三:
class Teacher {
var completion: ((Int)->Void)?
// 可选型的闭包是隐式逃逸闭包(sil也不能看出来的)
func makeIncrement(_ hander: ((Int)->Void)?) {
self.completion = hander
}
func doSomething() {
makeIncrement() { result in
print(result)
}
}
}
2.非逃逸闭包
除非标记了@escaping 或者可选型闭包
,系统默认都是非逃逸闭包
。它的声明周期是确定的(函数调用完,那闭包的声明周期就结束了),那就意味着非逃逸闭包捕获的局部变量无需在堆区开辟内存空间。
- 不会产生循环引用,
- 闭包所在的函数作用域内释放
- 编译器更多性能优化 (没有retain / release)
- 上下文的内存保存再栈上,不是堆上
func testNoEscaping(_ f: (() -> Void)?){
f?()
}
func test() -> Int{
var age = 20
testNoEscaping {
age += 30
}
return age
}
print(test()) // 50
编译成IR代码:
define hidden swiftcc i64 @"$s4main4testSiyF"() #0 {
entry:
%age.debug = alloca %TSi*, align 8
%0 = bitcast %TSi** %age.debug to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
%access-scratch = alloca [24 x i8], align 8
%1 = call noalias %swift.refcounted* @swift_allocObject(%swift.type* getelementptr inbounds (%swift.full_boxmetadata, %swift.full_boxmetadata* @metadata, i32 0, i32 2), i64 24, i64 7) #1
%2 = bitcast %swift.refcounted* %1 to <{ %swift.refcounted, [8 x i8] }>*
%3 = getelementptr inbounds <{ %swift.refcounted, [8 x i8] }>, <{ %swift.refcounted, [8 x i8] }>* %2, i32 0, i32 1
%4 = bitcast [8 x i8]* %3 to %TSi*
store %TSi* %4, %TSi** %age.debug, align 8
%._value = getelementptr inbounds %TSi, %TSi* %4, i32 0, i32 0
store i64 20, i64* %._value, align 8
%5 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #1
%6 = ptrtoint %swift.refcounted* %1 to i64
call swiftcc void @"$s4main14testNoEscapingyyyycSgF"(i64 ptrtoint (void (%swift.refcounted*)* @"$s4main4testSiyFyycfU_TA" to i64), i64 %6)
call void @"$sIeg_SgWOe"(i64 ptrtoint (void (%swift.refcounted*)* @"$s4main4testSiyFyycfU_TA" to i64), i64 %6)
%7 = bitcast [24 x i8]* %access-scratch to i8*
call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7)
%8 = bitcast %TSi* %4 to i8*
call void @swift_beginAccess(i8* %8, [24 x i8]* %access-scratch, i64 32, i8* null) #1
%._value1 = getelementptr inbounds %TSi, %TSi* %4, i32 0, i32 0
%9 = load i64, i64* %._value1, align 8
call void @swift_endAccess([24 x i8]* %access-scratch) #1
%10 = bitcast [24 x i8]* %access-scratch to i8*
call void @llvm.lifetime.end.p0i8(i64 -1, i8* %10)
call void @swift_release(%swift.refcounted* %1) #1
ret i64 %9
}
testNoEscaping
捕获了age并没有在堆区开辟内存空间。
3.自动闭包
自动闭包的定义:
是一种用来把实际参数传递给函数表达式打包的闭包,不接受任何实际参数,当其调用是,返回内部表达式的值。
好处:用普通表达式代替闭包的写法,语法糖的一种
func debugOutPrint(_ condition: Bool , _ message: @autoclosure () -> String){
if condition {
print("lg_debug:\(message())")
}
}
func dosomthing() -> String{
//耗时的操作
return "Application Error Occured"
}
//String, () -> String
// {} -> String
debugOutPrint(true, dosomthing)
debugOutPrint(true, { return dosomthing() })
message参数既可以接收String,也可以接收 ()->String