前言
本节内容需要分析IR代码,语法可参考LLVM语言参考手册。
一、闭包是什么
闭包是可以在代码中被传递和引用的功能性独立代码块。闭包可以捕获和存储其所在上下文中任意常量和变量的引用
,这就是所谓的闭合并包裹那些常量和变量,因此被称为“闭包”。 作为一种优化,如果一个值没有改变或者在闭包的外面,Swift 可能会使用这个值的拷贝而不是捕获
。Swift 会为你管理在捕获过程中涉及到的所有内存操作。
闭包采用如下三种形式之一:
-
全局函数
一个有名字
但不会捕获任何值
的闭包 (对标NSGlobalBlock?) -
嵌套函数
是一个有名字
并可以捕获其封闭函数域内值
的闭包 -
闭包表达式
是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值
的匿名
闭包。闭包表达式语法一般形式如下:
{ (parameters) -> return type in statements }
下面以sorted()方法为例来对一般语法进行简化
//一般写法 sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 }) //从语境中推断类型 sorted(by: { s1, s2 in return s1 > s2 } ) //从单表达式闭包隐式返回 sorted(by: { s1, s2 in s1 > s2 } ) //简写的实际参数名 sorted(by: { $0 > $1 } ) //运算符函数 sorted(by: >) //尾随闭包 sorted() { $0 > $1 } sorted { $0 > $1 }
闭包是引用类型
,可传递给函数做参数。
逃逸闭包
:当闭包作为一个实际参数传递给一个函数的时候,我们就说这个闭包逃逸了,因为它是在函数返回之后调用
的。用@escaping
在形式参数前修饰。主要场景为延时调用
,如接口回调、GCD延时。自动闭包
:一种自动创建的用来把作为实际参数传递给函数的表达式打包
的闭包。它不接受任何实际参数
,并且当它被调用时,它会返回内部
打包的表达式的值
。这个语法的好处在于通过写普通表达式代替显式闭包而使你省略包围函数形式参数的括号。用@autoclosure
在形式参数前修饰。滥用自动闭包会导致你的代码难以读懂。上下文和函数名应该写清楚求值是延迟的。
二、IR分析
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
var makeInc = makeIncrementer(forIncrement: 10)
var value = makeInc()
以官方的这个代码为例,通过swiftc -emit-ir main.swift > main.ll
转为IR代码,下面选取一部分代码进行解释
数据结构定义
%swift.function = type {
i8*, %swift.refcounted* }
%swift.full_boxmetadata = type {
void (%swift.refcounted*)*, i8**, %swift.type, i32, i8* }
%swift.refcounted = type {
%swift.type*, i64 }
%swift.type = type {
i64 }
%TSi = type <{
i64 }>
全局变量
@"symbolic Si" = linkonce_odr hidden constant <{
[2 x i8], i8 }> <{
[2 x i8] c"Si", i8 0 }>, section "__TEXT,__swift5_typeref, regular, no_dead_strip", align 2
@"\01l__swift5_reflection_descriptor" = private constant {
i32, i32, i32, i32 } {
i32 1, i32 0, i32 0, i32 trunc