从Block探究swift闭包的底层实现

558 阅读3分钟

前言

本文主要记录研究的结果,暂时没具体说明研究的过程。

简介

闭包(Closures)是自包含的功能代码块,可以在代码中使用或者用来作为参数传值。

Swift中的闭包对应Objective-C语言中Block,本文基于对Block底层的理解,来探讨swift闭包实现底层。

代码层

先从代码的层面,对比Blockswift闭包的不同点,来理解swift闭包

语法

代码例子

objc

int c = 3;
int (^sum)(int, int) = ^(int a, int b) {
    return a + b + c;
};
int r = sum(1, 2);   // r = 6    a = 1, b = 2, c = 3

swift

var c = 3;
let sum = {(_ a: Int, _ b: Int) -> Int in
   return a + b + c;
}
let r = sum(1, 2);   // r = 6    a = 1, b = 2, c = 3

小结

从上述最基本的代码可以看到两种语言闭包写法上的不同,大致如下

objcswift
语法^ 返回值类型 (参数列表) {表达式}{ (参数列表) -> 返回值类型 in 表达式 }

变量捕获

代码例子

objc

int c = 3;           // 拷贝
__block int d = 4;   // 引用
int e = 5;           // 拷贝
int (^sum)(int, int) = ^(int a, int b) {
    // c += 1;  // 不支持修改
    d += 1;
    return a + b + c + d + e;
};
e += 1;   // 不影响sum执行的结果
int r1 = sum(1, 2);   // r1 = 16   a = 1, b = 2, c = 3, d = 5, e = 5
int r2 = sum(1, 2);   // r1 = 17   a = 1, b = 2, c = 3, d = 6, e = 5

swift

var c = 3;   // 拷贝
var d = 4;   // 引用
var e = 5;   // 引用
let sum = {(_ a: Int, _ b: Int) -> Int in
   // c += 1;  // 支持修改,但捕获类型会变成引用
   d += 1;
   return a + b + c + d + e;
}
e += 1;
let r1 = sum(1, 2);   // r1 = 17   a = 1, b = 2, c = 3, d = 5, e = 6
let r2 = sum(1, 2);   // r1 = 18   a = 1, b = 2, c = 3, d = 6, e = 6

小结

从上述代码的运行结果,可以得到以下结论

objcswift
拷贝只读,不允许修改变量只读,不对变量进行修改
引用使用关键字 __block 修饰修改变量,由编译器推断

底层实现

接下来,从底层结构代码来看Blockswift闭包

Objc

数据结构

Block

// Block在内存中的数据结构
typedef void(*BlockInvokeFunction)(void *, ...);
struct Block_layout {
    void *  isa;  // 同类对象的isa指针,但不用于引用计数等
    int32_t flags; // 包含引用计数等信息
    int32_t reserved;
    BlockInvokeFunction invoke;   // 函数地址指针
    struct Block_descriptor *descriptor;   // Block_layout的描述数据,大小不固定,根据flags的标识位判断
    // 捕获的变量
};

struct Block_descriptor {
    uintptr_t reserved;
    uintptr_t size;   // Block_layout的具体大小
    
/*
typedef void(*BlockCopyFunction)(void *, const void *);
typedef void(*BlockDisposeFunction)(const void *);
struct Block_descriptor_2 {
    //requires BLOCK_HAS_COPY_DISPOSE
    BlockCopyFunction copy;   // 复制函数
    BlockDisposeFunction dispose;  // 析构函数
};
*/

/*
struct Block_descriptor_3 {
    // requires BLOCK_HAS_SIGNATURE
    const char *signature;
    const char *layout;
};
*/
};

引用类型

struct Block_byref {
    void *isa;   // null
    struct Block_byref *forwarding;   // 指向最终的数据
    volatile int32_t flags; // contains ref count
    uint32_t size;
/*
typedef void(*BlockByrefKeepFunction)(struct Block_byref*, struct Block_byref*);
typedef void(*BlockByrefDestroyFunction)(struct Block_byref *);
struct Block_byref_2 {
    // requires BLOCK_BYREF_HAS_COPY_DISPOSE
    BlockByrefKeepFunction byref_keep;
    BlockByrefDestroyFunction byref_destroy;
};
*/

/*
struct Block_byref_3 {
    // requires BLOCK_BYREF_LAYOUT_EXTENDED
    const char *layout;
};
*/

    // 引用数据
};

函数签名

returntype (*BlockInvokeFunction)(struct Block_layout *, ... /*param list*/)

内存分布例子

image.png

swift

数据结构

Swift闭包

// swift闭包的结构体
// 目前没在源码中找到对应的声明
// 下面的swift.function是通过分析IR代码得出
struct swift.function {  
    void *function;    // 函数地址指针
    HeapObject *obj;   // 捕获的变量, 
}
struct HeapObject {
    HeapMetadata const *metadata;  // 闭包对应是TargetHeapLocalVariableMetadata
    InlineRefCounts refCounts;  // 包含引用计数等信息
    // 捕获的变量
}

// 闭包的Metadata结构体
// 函数签名的描述
struct TargetFunctionTypeMetadata {
    StoredPointer Kind;
    TargetFunctionTypeFlags<StoredSize> Flags;
    ConstTargetMetadataPointer<Runtime, swift::TargetMetadata> ResultType;
    // TargetMetadata *[]   // Parameters
    // ParameterFlags[]
}

PS: HeapObjectswift用存储堆对象的结构,并负责引用计数等相关的功能,类似与Objc上的class对象HeapObject覆盖的范围更广,支持classstructenum等类型。

引用类型

// 引用类型出差
struct HeapObject {
    HeapMetadata const *metadata;  // 闭包对应是TargetHeapLocalVariableMetadata
    InlineRefCounts refCounts;     // 包含引用计数等信息
    // 引用数据
}
// 用于存储闭包捕获的变量和引用类型
// 只有destroy函数,没有witness table
struct TargetHeapLocalVariableMetadata {
  uintptr_t Kind;                   // 区分类型,从TargetMetadata继承
  uint32_t OffsetToFirstCapture;    // 存储数据的起始位置
  const void *CaptureDescription;
};

函数签名

returntype SwiftInvokeFunction(... /*param list*/, struct HeapObject *)

内存分布例子

image.png