Swift進階-類與結(jié)構(gòu)體
Swift-函數(shù)派發(fā)
Swift進階-屬性
Swift進階-指針
Swift進階-內(nèi)存管理
Swift進階-TargetClassMetadata和TargetStructMetadata數(shù)據(jù)結(jié)構(gòu)源碼分析
Swift進階-Mirror解析
Swift進階-閉包
Swift進階-協(xié)議
Swift進階-泛型
Swift進階-String源碼解析
Swift進階-Array源碼解析
一、函數(shù)類型
函數(shù)本身也有自己的類型,它由 形參類型和返回類型 組成。
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))

上面調(diào)試結(jié)果可以看出來:
- 函數(shù)類型是引用類型;
- a指向的地址第一個8字節(jié)存儲的是函數(shù)類型。
函數(shù)類型是引用類型的案例:
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
// 理解成每次都在創(chuàng)建內(nèi)部函數(shù)對象
print(makeIncrementer()()) // 11
print(makeIncrementer()()) // 11
print(makeIncrementer()()) // 11
函數(shù)類型的元數(shù)據(jù)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;作用是標識了函數(shù)的類型;

-
ConstTargetMetadataPointer<Runtime, swift::TargetMetadata> ResultType;是返回值類型
4.其實TargetFunctionTypeMetadata還擁有一個參數(shù)列表,它其實是一個連續(xù)的內(nèi)存空間

于是乎可以分析出TargetFunctionTypeMetadata的數(shù)據(jù)結(jié)構(gòu):
struct TargetFunctionTypeMetadata{
var kind: Int // isa
var flags: Int
var resultType: UnsafeRawPointer // 返回值類型
var arguments: ArgumentsBuffer<Any.Type> // 參數(shù)類型列表
// 獲取參數(shù)個數(shù)
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))
}
}
}
于是乎驗證一下推測來的數(shù)據(jù)結(jié)構(gòu)對不對:
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.閉包概念
閉包是一個捕獲了上下文的常量/變量的函數(shù)。
可以看一個官方給的案例:
func makeIncrementer() -> () -> Int {
var runningTotal = 10
func incrementer() -> Int {
runningTotal += 1
return runningTotal
}
return incrementer
}
var a = makeIncrementer()
這里incrementer作為一個閉包,顯然他是一個函數(shù),其次為了保證其執(zhí)行,要捕獲外部變量runningTotal到內(nèi)部,所以閉包的關鍵就有捕獲外部變量或常量和函數(shù)。
2.閉包表達式
閉包在語法上有這樣的標準結(jié)構(gòu): {(參數(shù)列表) -> 返回值 in 閉包體}
{ (param) -> (returnType) in
//do somethings
}
- 作用域(也就是大括號)
- 參數(shù)和返回值
- 函數(shù)體in之后的代碼
2.1 閉包即可以當做變量
// 也可以用可選性修飾 var closure: (String)->String = {...}
var closure = { (name: String) in
return name
}
print(closure("安老師"))
2.2 閉包即可以當做函數(shù)的參數(shù)
// 如果用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 閉包即可以當做函數(shù)的返回值
func makeIncrementer() -> () -> Int {
var runningTotal = 10
func incrementer() -> Int {
runningTotal += 1
return runningTotal
}
return incrementer
}
print(makeIncrementer()())
3.尾隨閉包
當我們把閉包表達式作為函數(shù)的最后一個參數(shù),如果當前的閉包表達式很長,我們可以通過尾隨 閉包的書寫方式來提高代碼的可讀性。
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)
})
閉包表達式的好處 有很多:
- 利用上下文推斷參數(shù)和返回值類型
- 單表達式可以隱士返回,既省略 return 關鍵字
- 參數(shù)名稱的簡寫(比如我們的 $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類型區(qū)分:全局、棧、堆; -
swift的閉包都沒有這種概念了,var closure = { print("closure") } 中的closure存儲的是type metadata。
4.1 Objective-C 的 Block 類型
全局Block:不捕獲外部變量(只使用靜態(tài)變量、全局變量);
棧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轉(zhuǎn)化為堆block,o還被引用了1次,引用計數(shù)還得+1)
void(^copyBlock)(void) = [weakBlock copy];
copyBlock(); // 5
}
// 1 3 4 5
4.2 block與閉包在捕獲外部變量時的特性區(qū)別
- (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 內(nèi)部捕獲的值,我們只需要對當前的 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
區(qū)別:當閉包在使用 i 的時候,直接使用外部全局變量取 i 的值??纯?code>sil:

上面的案例舉的全局變量案例,那如果把上面的代碼放到函數(shù)體里呢?
func test(){
var i = 1
let closure = {
print("closure:\(i)")
}
i += 1
print("before closure:\(i)") // 2
closure() // 2
print("after closure:\(i)") // 2
}
可以發(fā)現(xiàn),打印結(jié)果沒什么變化??赐?code>sil再下結(jié)論:


project_box與之對應的是alloc_box;alloc_box在捕獲的時候會在堆區(qū)上開辟內(nèi)存空間以存儲變量 i 的值;project_box是用到該值的時候,從堆上取出,再對其取值。
所以閉包在堆區(qū)開辟內(nèi)存空間的時候,有Metadata、Refcount 和 i。
總結(jié):
1.閉包是一個引用類型;
2.閉包捕獲全局(靜態(tài))變量/常量默認是不捕獲的,直接使用該變量/常量地址;
3.閉包捕獲局部值類型變量會在堆區(qū)開辟內(nèi)存空間;每次修改捕獲變量的值的時候,其實是修改堆區(qū)當中的值;
4.閉包捕獲局部引用類型變量,無需開辟堆區(qū)內(nèi)存空間,直接可引用堆區(qū)地址;并且該變量引用計數(shù)+1,在閉包生命周期結(jié)束會把該變量引用計數(shù)-1。
5.閉包的本質(zhì)
5.1 熟悉IR語法:
數(shù)組
[<elementnumber> x <elementtype>] // 元素個數(shù) x 元素類型
//example:
alloca [24 x i8], align 8 24個i8都是0
alloca [4 x i32] === array
結(jié)構(gòu)體
%swift.refcounted = type { %swift.type*, i64 }
//example:
%T = type {<type list>} //這種和C語言的結(jié)構(gòu)體類似
指針類型
<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;
}
// 取出結(jié)構(gòu)體首地址
getelementptr inbounds %struct.munger_struct, %struct.munger_struct %1, i64 0
// 1.取出結(jié)構(gòu)體首地址,2.然后取出結(jié)構(gòu)體第一個元素
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

總結(jié):
第一個索引不會改變返回的指針的類型,也就是說ptrval前面的*對應什么類型,返回就是什 么類型
第一個索引的偏移量的是由第一個索引的值和第一個ty指定的基本類型共同確定的。
后面的索引是在數(shù)組或者結(jié)構(gòu)體內(nèi)進行索引
每增加一個索引,就會使得該索引使用的基本類型和返回的指針的類型去掉一層
5.2 閉包的數(shù)據(jù)結(jié)構(gòu)
把下面代碼編譯成IR,分析還原出閉包的數(shù)據(jù)結(jié)構(gòu)
func makeIncrementer() -> () -> Int {
var runningTotal = 10
func incrementer() -> Int {
runningTotal += 1
return runningTotal
}
return incrementer
}
let increm = makeIncrementer()
找到main函數(shù)

打開終端輸入,還原混寫名稱
$ xcrun swift-demangle s4main15makeIncrementerSiycyF
// ---------下面是輸出結(jié)果---------------
$s4main15makeIncrementerSiycyF ---> main.makeIncrementer() -> () -> Swift.Int

所以swift.refcounted是一個 {i64, i64}的結(jié)構(gòu)體
所以對于我們調(diào)用makeIncrementer()的返回值 { i8*, %swift.refcounted* } 就可以分析出閉包的最初始的數(shù)據(jù)結(jié)構(gòu):
struct ClosureData {
var unkown: UnsafeRawPointer // i8*
var closureHeapObject: ClosureHeapObject // swift.refcounted
}
struct ClosureHeapObject {
var metadata: UnsafeRawPointer
var refcount1: Int32
var refcount2: Int32
}
接下來我們看看 s4main15makeIncrementerSiycyF就是makeIncrementer函數(shù)到底是怎么構(gòu)造出返回的東西 { 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堆空間的內(nèi)存地址
%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
// 做了一個指針類型的轉(zhuǎn)換,相當于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
// 把它轉(zhuǎn)換成 %TSi* 類型 (%TSi* 其實是一個 type<{ i64 }> 64位的結(jié)構(gòu)體)
%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字節(jié)的內(nèi)存空間里
store i64 10, i64* %._value, align 8
// 處理引用計數(shù)相關
%5 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %1) #1
call void @swift_release(%swift.refcounted* %1) #1
// 將func里的內(nèi)嵌閉包地址轉(zhuǎn)換成 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
}
根據(jù)上面的IR代碼,還原出 ClosureData 的數(shù)據(jù)結(jié)構(gòu)為:
struct ClosureData<Box> {
var ptr: UnsafeRawPointer // 閉包地址
var box: UnsafePointer<Box>
}
struct Box<T> {
var object: HeapObject // 實例對象的內(nèi)存地址
var value: T
}
///實例對象的內(nèi)存地址
struct HeapObject {
var metadata: UnsafeRawPointer
var refcount1: Int32
var refcount2: Int32
}
結(jié)論:ClosureData 的數(shù)據(jù)結(jié)構(gòu) = 閉包的執(zhí)行地址 + 捕獲變量堆空間的地址
下面是驗證結(jié)論代碼:
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)
// 內(nèi)存綁定
let closure_ptr = ptr.withMemoryRebound(to: ClosureData<Box<Int>>.self, capacity: 1){ $0.pointee }
print("閉包地址: \(closure_ptr.ptr)")
print("實例對象的內(nèi)存地址: \(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 可執(zhí)行文件的路徑 | grep 0000000100005700
// 輸出了一個混寫的名稱,把混寫名稱還原
$ xcrun swift-demangle 混寫的名稱
// 最后得到下面的內(nèi)容
// $s11SwiftTest15makeIncrementerSiycyF11incrementerL_SiyFTA ---> partial apply forwarder for incrementer #1 () -> Swift.Int in SwiftTest.makeIncrementer() -> () -> Swift.Int
如果 makeIncrementer 有參數(shù)呢?那么Box的數(shù)據(jù)結(jié)構(gòu)就變了:
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統(tǒng)一當成object的屬性
var value1: UnsafePointer<T1>
var value2: T2
}
驗證Box數(shù)據(jù)結(jié)構(gòu)的改變
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("參數(shù)的值:\(closure_ptr.box.pointee.value2)")
print("end")
func_ptr.deinitialize(count: 3)
func_ptr.deallocate()

三、C與Swift的函數(shù)調(diào)用
新建一個對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文件聲明的函數(shù)是否有被倒入到Swift:


還需要再橋接文件(ProjectName-Bridging-Header.h)上倒入.h: #import "TestC.h";
在swift中就可以調(diào)用這個函數(shù):
let result = TesctCFUnction { $0 + $1 }
print(result) // 30
但是,如果給定一個已經(jīng)聲明的閉包作為參數(shù)呢?它會報錯!

這個時候需要用到 @convention 關鍵字,意思是把swift閉包轉(zhuǎn)換成c語言的回調(diào)函數(shù)
let closure: @convention(c)(Int32, Int32) -> Int32 = {(a: Int32, b: Int32) -> Int32 in
return a + b
}
let result = TesctCFUnction(closure)
print(result) // 30
四、逃逸閉包、非逃逸閉包與自動閉包
1.逃逸閉包
逃逸閉包的定義:
當閉包作為一個實際參數(shù)傳遞給一個函數(shù)的時候,并且是在函數(shù)返回之后調(diào)用,我們就說這個閉包逃逸了。當我們聲明一個接受閉包作為形式參數(shù)的函數(shù)時,你可以在形式參數(shù)前寫 @escaping 來明確閉包是允許逃逸的。
- 作為函數(shù)的參數(shù)傳遞
- 當前閉包在函數(shù)內(nèi)部異步執(zhí)行或者被存儲
- 函數(shù)結(jié)束,閉包被調(diào)用,生命周期結(jié)束
情況一:
// 用屬性去存儲閉包
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 或者可選型閉包,系統(tǒng)默認都是非逃逸閉包。它的聲明周期是確定的(函數(shù)調(diào)用完,那閉包的聲明周期就結(jié)束了),那就意味著非逃逸閉包捕獲的局部變量無需在堆區(qū)開辟內(nèi)存空間。
- 不會產(chǎn)生循環(huán)引用,
- 閉包所在的函數(shù)作用域內(nèi)釋放
- 編譯器更多性能優(yōu)化 (沒有retain / release)
- 上下文的內(nèi)存保存再棧上,不是堆上
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并沒有在堆區(qū)開辟內(nèi)存空間。
3.自動閉包
自動閉包的定義:
是一種用來把實際參數(shù)傳遞給函數(shù)表達式打包的閉包,不接受任何實際參數(shù),當其調(diào)用是,返回內(nèi)部表達式的值。
好處:用普通表達式代替閉包的寫法,語法糖的一種
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參數(shù)既可以接收String,也可以接收 ()->String