【iOS】Blocks
文章目录
前言
笔者最近看了小白书的Blocks的板块,这篇博客简单总结一下有关于Blocks的内容,笔者也是初次较为深入的学习Block。如有错误,请不吝赐教。
什么是Blocks
用小白本里的话来讲就是:带有自动变量(局部变量)的匿名函数。
我们认识Blocks从C语言的函数开始认识他:
int func(int count);
这是我们C语言的函数内容,这时候我们从C语言的函数指针开始:
int (*funptr)(int) = &func;
int result = funptr(1);
NSLog(@"%d", result);
这样我们可以通过函数指针不需要知道函数名都可以调用这个函数,而Block则可以运行我们直接使用匿名函数。我们首先要认识一下我们的C语言函数中可能用到的一些变量类型:
- 自动变量
- 函数的参数
- 静态局部变量
- 静态全局变量
- 全局变量
能在函数的多次调用之间传递值的有后面三者。虽然这些变量的作用域不同,但在整个程序当中,一个变量总保持在一个内存区域。因此,虽然多次调用函数,但该变量值总能保持不变,在任何时候以任何状态调用,使用的都是同样的变量值。
int buttonId;
void buttonCallback (int event)
{
printf ("buttonId:&d event=&d\n" , buttonId, event) ;
}
void setButtonCallbacks () {
for (int i = 0; i < 30; ++i) {
buttonid = 1;
setButtonCallback (BUTTON_IDOFFSET + i, &buttonCallback) ;
}
}
这里用到了一个回调函数,这个回掉函数有一个问题,那就是我们所有的最后的回掉结果都会是我们最后的那个置,但是如果我们把他变成函数内的变量在进行一个参数传递就不会出现那中问题了。
void buttonCallback (int event, int buttonId)
{
printf ("buttonId:&d event=&d\n" , buttonId, event) ;
}
用Block来写就相当于:
void setButtonCallbacks()
{
for (int i = 0; i < BUTTON_MAX; ++i) {
setButtonCallbackUsingBlock(BUTTON_IDOFFSET + i, ^(int event) {
printf("buttonId:&d event=%d\n", i, event);
});
}
}
Block又被称为闭包,在不同的语言中都有应用。
Block语法
这里我们认识一下语法格式:
^int(int count) {
return 1;
};
然后我们可以省略返回值类型:
^(int count) {
return 1;
};
如果我们不需要用到参数那么可以仅仅保留表达式:
^{
return 1;
};
Block类型变量
我们先看之前的函数指针的内容:
int (*funptr)(int) = &func;
int result = funptr(1);
NSLog(@"%d", result);
Block也是这样子应用的。
同样地,在Block 语法下,可将 Block 语法赋值给声明为 Block 类型的变量中。即源代码中一旦使用Block语法就相当于生成了可赋值给 Block 类型变量的“值”。Blocks 中由Block 语法生成的值也被称为“Block”。在有关Blocks 的文档中,“Block”既指源代码中的Block 语法,也指由Block 语法所生成的值。
int (^blk)(int);
与前面的使用函数指针的源代码对比可知,声明Block类型变量仅仅是将声明函数指针类型变量的“*”变为“^”。该Block类型变量与一般的C语言变量完全相同,可作为以下用途使用。
- 自动变量
- 函数参数
- 静态变量
- 静态全局变量
- 全局变量
我们这里就不多赘述,主要展示一下我们在函数中使用Block,Block作为参数:
void func(int (^blk)(int))
{
// 在函数体中,可以调用 blk 这个 Block。
}
Block作为返回值:
int (^func())(int) {
return ^(int count) {
return count + 1; };
}
这时候我们一般会采用typdef来定义不同的内容:
typedef int (^blk_t)(int);
这样让我们的代码更加直观,更方便我们去理解这种类似的代码
截获自动变量
int main(int argc, const char * argv[]) {
@autoreleasepool {
int val = 10;
int fmt = 15;
void (^blk)(void) = ^ {
NSLog(@"%ld %ld",val, fmt);
};
fmt = 20;
val = 30;
blk();
}
return 0;
}
该源代码中,Block语法的表达式使用的是之前声明的目动变量 fmt 和 val。 Blocks 中,Block表达式截获所使用的自动变量的值,即保存该自动变量的瞬间值。因为Block表达式保存了自动变量的值,所以在执行 Block 语法后,即使改写Block 中使用的自动变量的值也不会影响Block 执行时自动变量的值。该源代码就在 Block 语法后改写了 Block 中的自动变量 val 和 fmt。
下面我们一起看一下执行结果。
10 15
__block修饰符
如果我们想让我们的Block中的自动变量在Block中进行修改的,那么我们只需要给他加上__block的修饰符就可以了,这样就可以在Blcok中修改我们设置在外面的局部变量了。
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int val = 10;
int fmt = 15;
void (^blk)(void) = ^ {
val = 30;
NSLog(@"%ld %ld",val, fmt);
};
fmt = 20;
blk();
}
return 0;
}
30 15
Type: Notice | Timestamp: 2025-02-18 19:09:28.135305+08:00 | Process: BStudy | Library: BStudy | TID: 0x286c5b7
截获的自动变量
我们不能在block中变更我们的int值,那能不能给数组添加数据呢,答案是显而易见的,这是可以的:
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int val = 10;
int fmt = 15;
NSMutableArray* array = [@[] mutableCopy];
void (^blk)(void)