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 
struct TargetFunctionTypeMetadata : public TargetMetadata {
  using StoredSize = typename Runtime::StoredSize;
  using Parameter = ConstTargetMetadataPointer;

  TargetFunctionTypeFlags Flags;

  /// The type metadata for the result type.
  ConstTargetMetadataPointer ResultType;

  Parameter *getParameters() { return reinterpret_cast(this + 1); }

  const Parameter *getParameters() const {
    return reinterpret_cast(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);

  static bool classof(const TargetMetadata *metadata) {
    return metadata->getKind() == MetadataKind::Function;
  }

  uint32_t *getParameterFlags() {
    return reinterpret_cast(getParameters() + getNumParameters());
  }

  const uint32_t *getParameterFlags() const {
    return reinterpret_cast(getParameters() +
                                              getNumParameters());
  }

  TargetFunctionMetadataDifferentiabilityKind *
  getDifferentiabilityKindAddress() {
    assert(isDifferentiable());
    void *previousEndAddr = hasParameterFlags()
        ? reinterpret_cast(getParameterFlags() + getNumParameters())
        : reinterpret_cast(getParameters() + getNumParameters());
    return reinterpret_cast<
        TargetFunctionMetadataDifferentiabilityKind *>(
        llvm::alignAddr(previousEndAddr,
                        llvm::Align(alignof(typename Runtime::StoredPointer))));
  }

  TargetFunctionMetadataDifferentiabilityKind
  getDifferentiabilityKind() const {
    if (isDifferentiable()) {
      return *const_cast *>(this)
          ->getDifferentiabilityKindAddress();
    }
    return TargetFunctionMetadataDifferentiabilityKind
        ::NonDifferentiable;
  }

  ConstTargetMetadataPointer *
  getGlobalActorAddr() {
    assert(hasGlobalActor());
    
    void *endAddr =
        isDifferentiable()
          ? reinterpret_cast(getDifferentiabilityKindAddress() + 1) :
        hasParameterFlags()
          ? reinterpret_cast(getParameterFlags() + getNumParameters()) :
        reinterpret_cast(getParameters() + getNumParameters());
    return reinterpret_cast<
        ConstTargetMetadataPointer *>(
          llvm::alignAddr(
              endAddr, llvm::Align(alignof(typename Runtime::StoredPointer))));
  }

  ConstTargetMetadataPointer
  getGlobalActor() const {
    if (!hasGlobalActor())
      return ConstTargetMetadataPointer();

    return *const_cast *>(this)
      ->getGlobalActorAddr();
  }
};
using FunctionTypeMetadata = TargetFunctionTypeMetadata;
  1. TargetFunctionTypeMetadata继承自TargetMetadata说明它拥有Kind属性(也就是isa);
  2. TargetFunctionTypeFlags Flags; 作用是标识了函数的类型;
TargetFunctionTypeFlags
  1. ConstTargetMetadataPointer ResultType;是返回值类型

4.其实TargetFunctionTypeMetadata还拥有一个参数列表,它其实是一个连续的内存空间

parameters

于是乎可以分析出TargetFunctionTypeMetadata的数据结构:

struct TargetFunctionTypeMetadata{
    var kind: Int // isa
    var flags: Int 
    var resultType: UnsafeRawPointer // 返回值类型
    var arguments: ArgumentsBuffer // 参数类型列表
    // 获取参数个数
    func numberArguments() -> Int {
        return self.flags & 0x0000FFFF
    }
}

struct ArgumentsBuffer{
    var element: Element

    mutating func buffer(n: Int) -> UnsafeBufferPointer {
        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 {
        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.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-CBlock 类型区分:全局、栈、堆
  • swift闭包 都没有这种概念了,var closure = { print("closure") } 中的closure存储的是type metadata。

4.1 Objective-CBlock 类型
全局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

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再下结论:

找到test声明
找到closure

project_box与之对应的是alloc_boxalloc_box在捕获的时候会在堆区上开辟内存空间以存储变量 i 的值;project_box是用到该值的时候,从堆上取出,再对其取值。
所以闭包在堆区开辟内存空间的时候,有Metadata、Refcount 和 i。

总结:
1.闭包是一个引用类型;
2.闭包捕获全局(静态)变量/常量默认是不捕获的,直接使用该变量/常量地址;
3.闭包捕获局部值类型变量会在堆区开辟内存空间;每次修改捕获变量的值的时候,其实是修改堆区当中的值;
4.闭包捕获局部引用类型变量,无需开辟堆区内存空间,直接可引用堆区地址;并且该变量引用计数+1,在闭包生命周期结束会把该变量引用计数-1。

5.闭包的本质

5.1 熟悉IR语法:
数组

[ x ]   // 元素个数 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 {} //这种和C语言的结构体类似

指针类型

 *

//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 {
    var ptr: UnsafeRawPointer // 闭包地址
    var box: UnsafePointer
}

struct Box {
    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.allocate(capacity: 1)
ptr.initialize(to: closure)

// 内存绑定
let closure_ptr = ptr.withMemoryRebound(to: ClosureData>.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 {
    var object: HeapObject  // 把value1、value2统一当成object的属性
    var value1: UnsafePointer
    var value2: T2
}

验证Box数据结构的改变

let funcStruct = NoMeanStruct(closure: makeIncrementer(20))
let func_ptr = UnsafeMutablePointer.allocate(capacity: 1)
func_ptr.initialize(to: funcStruct)

let closure_ptr = func_ptr.withMemoryRebound(to: ClosureData>.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 
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

你可能感兴趣的:(Swift进阶-闭包)