翻了很多文章,发现关于Swift闭包关于上下文变量捕获这块,都没有说的很详细,或者Swift2这样的老版本已经不适用了,问了GPT也是和自己实验的结果不一样,记录下来。

一:OC的block

首先,回顾一下OC中的block。 block对局部变量基本数据类型的捕获,是在创建时捕获了值,并保存副本在自己的结构体中,修改也是修改副本,不会影响到原本的值。 例子:

typedef void (^MyBlock)(void);

- (MyBlock)createBlock {

int number = 10;

MyBlock block = ^{

NSLog(@"Captured value: %d", number);

};

number = 20;

return block;

}

- (void)executeBlock {

MyBlock myBlock = [self createBlock];

myBlock();

}

最后输出是:

Captured value: 10

二:__block修饰符

如果希望block内部修改的值是原本的值,或者希望block捕获的值后面还会变化,需要对原本的变量添加__block修饰符。

typedef void (^MyBlock)(void);

- (MyBlock)createBlock {

__block int number = 10;

MyBlock block = ^{

NSLog(@"Captured value: %d", number);

};

number = 20;

return block;

}

- (void)executeBlock {

MyBlock myBlock = [self createBlock];

myBlock();

}

最后输出是:

Captured value: 20

三:Swift闭包

那Swift闭包对局部基本数据类型的变量的捕获是怎样的呢?Swift中没有__block修饰符,是不是就没法做到block那样的功能了呢? 答案是否定的,例子:

var i = 0

let closure = {

print("\(i)")

}

i += 1

print("\(i)")

closure()

print("\(i)")

输出结果是什么呢? 第一个输出是1,很好理解。 第二个输出是闭包里的i,输出多少呢? 第三个输出是1,也好理解。 看答案: 闭包内输出是1 ,这好像和OC中block是不一样的?接着往下看。

顺便,很无奈的是,GPT给的答案也是错误的:

那么,看上去和OC中捕获__block修饰符的int是一样的?继续尝试:

var i = 0

let closure = {

print("\(i)")

i += 1

}

i += 1

print("\(i)")

closure()

print("\(i)")

输出结果是什么呢? 第一个输出是1,很好理解。 第二个输出是闭包里的i,是1,刚才已经看到了。 第三个输出输出多少呢? 看答案: 答案是2。说明闭包内修改的值,也会反应到闭包外部。

GPT给的答案也是错误的:

再看一个例子:

var i = 0

let closure = {

print("\(i)")

}

i += 1

closure()

i += 1

closure()

i += 1

closure()

输出会是什么呢? 所以这几个例子都证明了,闭包对变量进行捕获,是将变量复制到了堆上,之后不论是闭包内,还是闭包外,操作的值,都是堆上的这个值,闭包对这个值强持有。

捕获值的本质是将变量存储到堆上。

1、一个闭包能够从上下文捕获已经定义的常量和变量,并且能够在其函数体内引用和修改这些值,即使这些定义的常量和变量的原作用域不存在; 2、修改捕获值实际是修改堆区中的value值; 3、当每次重新执行当前函数时,都会重新创建内存空间。

四:捕获列表

那么怎么做到,在定义时就确定捕获的值呢?就像block那样,没有__block修饰符的int? 使用捕获列表 [] in 很容易理解,不再赘述。

好文阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: