【iOS】Blocks
创始人
2024-05-29 02:33:42
0

Block

  • Blocks概要
    • 什么是Blocks?
    • Block语法
    • Block类型变量
    • 截获自动变量值
    • __block说明符
  • Blocks的实现
    • Block的实质

Blocks概要

什么是Blocks?

Blocks可简单概括为:

带有自动变量(局部变量)的匿名函数

在使用Blocks时,可以不声明C++类和Objective-C类,也没有使用静态变量、静态全局变量或全局变量时的问题,仅用编写C语言函数的源代码量即可使用带有自动变量值的匿名函数。

对于“带有自动变量值的匿名函数”这一概念并不仅指Blocks。它还存在于许多其他程序语言中。在计算机科学中,此概念也称为闭包(Closure)、lambda计算(λ计算)等。

请添加图片描述

Block语法

完整的Block语法与一般的C语言函数定义相比,仅有两点不同。

  1. 没有函数名
  2. 带有“^”

^:
由于OS X、iOS应用程序的源代码中将大量使用Block,所以插入该记号便于查找。请添加图片描述

例如可写出下面形式的Block语法:

^int (int count) {return count + 1;}

当省略返回值类型时:

^(int count) {return count + 1;}

省略返回值类型时,如果表达式中与return语句就使用该返回值的类型;如果没有return语句就使用void类型;当表达式中含有多个return语句时,所有return的返回值类型必须相同。

如果不使用参数,参数列表也可省略:

^void (void) {printf("Blocks\n");}

上述代码可以省略为:

^{printf("Blocks\n");}

Block类型变量

声明block类型变量的实例如下:

int (^blk) (int);

使用block语法将Block赋值给Block类型变量:

int (^blk) (int) = ^int (int count) {return count + 1;};

由"^"开始的 Block 语法生成的 Block 被赋值给变量 blk 中。因为与通常的变量相同,所以当然也可以由Block类型变量向 Block 类型变量赋值:

int(^blk1)(int) = blk;
int (^blk2)(int);
blk2 = blk1;

在函数参数中使用 Block类型变量可以向函数传递 Block。

void func(int (^blk)(int)) {//...}

在函数返回值中指定 Block 类型,可以将 Block 作为函数的返回值返回。

int (^func()(int))return ^(int count){return count + 1;};
}

由此可知,在函数参数和返回值中使用 Block 类型变量时,记述方式极为复杂。这时,我们可以像使用函数指针类型时那样,使用 typedef 来解决该问题。

typedef int (^blk t)(int);

如上所示,通过使用 typedef可声明"blk t"类型变量。我们试着在以上例子中的函数参数和函数返回值部分里使用一下。

/*原来的记述方式
void func(int (^blk)(int) )
*/void func (blk_t blk) {/* 原来的记述方式
int(^func ()(int))
*/blk_t func() {

通过 Block类型变量调用Block与C语言通常的函数调用没有区别。在函数参数中使用Block类型变量并在函数中执行 Block 的例子如下:

int func(blk_t blk,int rate) {return blk (rate);
}

当然,在 Objective-C的方法中也可使用。

-(int) methodUsingBlock:(blk_t)blk rate:(int)rate {return blk (rate);
}

截获自动变量值

int main() {int dmy = 256;int val = 10;const char *fmt = "val = 号d\n";void (^b1k)(void) = ^{printf(fmt,val);};val = 2;fmt = "These values were changed. val = %d\n";blk();return 0;
}

结果是

val= 10

执行结果并不是改写后的值"These values were changed. val=2",而是执行 Block 语法时的自动变量的瞬间值。该Block语法在执行时,字符串指针"val=%d\n"被赋值到自动变量 fmt中,int 值10被赋值到自动变量 val中,因此这些值被保存(即被截获),从而在执行块时使用。
这就是自动变量值的截获。

__block说明符

实际上,自动变量值截获只能保存执行 Block 语法瞬间的值。保存后就不能改写该值。下面我们来尝试改写截获的自动变量值,看看会出现什么结果。下面的源代码中,Block 语法之前声明的自动变量 val 的值被赋予1。

int val=0
void ("blk)(void) = ^{val = 1;};
blk();
printf("val = %d\n",val);

以上为在 Block 语法外声明的给自动变量赋值的源代码。该源代码会产生编译错误。

error: variable is not assignable (missing __block type specifier)
void (^blk)(void)= ^{val = 1;};

若想在 Block 语法的表达式中将值赋给在 Block 语法外声明的自动变量,需要在该自动变量上附加 block 说明符。该源代码中,如果给自动变量声明 int val附加 block 说明符,就能实现在Block 内赋值。

_block int val = 0;
void (^blk)(void) = ^{val = 1;};
blk();
printf("val = d\n",val);

执行结果:

val =1

那么截获 Objective-C 对象,调用变更该对象的方法也会产生编译错误吗?

id array = [[NSMutableArray alloc] init];
void (^blk)(void)= ^{id obj =[[NSObject alloc] init];[array addObject:obj];
};

这样是不会出错的,但是如果是向截获的变量 array 赋值则会产生编译错误。该源代码中截获的变量值为NSMutableArray 类的对象。如果用C语言来描述,即是截获NSMutableArray 类对象用的结构体实例指针。虽然赋值给截获的自动变量 array 的操作会产生编译错误,但使用截获的值却不会有任何问题。

下面源代码向截获的自动变量进行赋值,因此会产生编译错误。

id array =[[NSMutableArray alloc] init];
void(^b1k)(void) = ^(
array =[[NSMutableArray alloc] init];
};
error: variable is not assignable(missingblock type specifier)
array = [[NSMutableArray alloc] init];

这种情况下,需要给截获的自动变量附加_block 说明符。

__block id array =[[NSMutableArray alloc] init];
void (^blk)(void) = ^{array = [[NSMutableArray alloc] init];
};

另外,在使用C语言数组时必须小心使用其指针。源代码示例如下∶

const char text[] = "hello";
void (^blk)(void) = ^{printf("%c\n", text[2]);
};

只是使用C语言的字符串字面量数组,而并没有向截获的自动变量赋值,因此看似没有问题。但实际上会产生以下编译错误∶

error; cannot refer to declaration with an array type inside block
printf("cAn",text[2]);note: declared here
const char text [] ="hello";

这是因为在现在的 Blocks 中,截获自动变量的方法并没有实现对C语言数组的截获。这时,使用指针可以解决该问题。

const char *text = "hello";
void (^blk)(void)= ^{printf("%c\n",text [2]);
}

Blocks的实现

Block的实质

Block是"带有自动变量值的匿名函数",但 Block究竟是什么呢?本节将通过Block 的实现进一步帮大家加深理解。

前几节讲的 Block 语法看上去好像很特别,但它实际上是作为极普通的C语言源代码来处理的。通过支持 Block 的编译器,含有Block 语法的源代码转换为一般C语言编译器能够处理的源代码,并作为极为普通的C 语言源代码被编译。
这不过是概念上的问题,在实际编译时无法转换成我们能够理解的源代码,但 clang(LLVM 编译器)具有转换为我们可读源代码的功能。通过"-rewrite-objc"选项就能将含有Block 语法的源代码变换为C++的源代码。说是C++,其实也仅是使用了struct结构,其本质是C语言源代码。

clang -rewrite-objc 源代码文件名

下面,我们转换 Block 语法。

int main() {void (^blk)(void) = ^{printf("Block\n");};blk();return 0;
}

此源代码的 Block 语法最为简单,它省略了返回值类型以及参数列表。该源代码通过 clang 可变换为以下形式∶

//经过clang转换后的C++代码
struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr;
};struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};static void __main_block_func_0(struct __main_block_impl_0 *__cself {printf("Block\n");
}static struct __main_block_desc_0 {size_t reserved;size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};int main(int argc, const char * argv[]) {void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);return 0;
}

相关内容

热门资讯

苹果系统安卓爱思助手,系统兼容... 你有没有发现,手机的世界里,苹果系统和安卓系统就像是一对欢喜冤家,总是各有各的粉丝,各有各的拥趸。而...
安卓系统占用很大内存,揭秘内存... 手机里的安卓系统是不是让你感觉内存不够用,就像你的房间堆满了杂物,总是找不到地方放新东西?别急,今天...
安卓系统p30,安卓系统下的摄... 你有没有发现,最近安卓系统P30在手机圈里可是火得一塌糊涂呢!这不,我就来给你好好扒一扒这款手机的那...
siri被安卓系统进入了,智能... 你知道吗?最近科技圈可是炸开了锅,因为一个大家伙——Siri,竟然悄悄地溜进了安卓系统!这可不是什么...
最强挂机系统和安卓区别,揭秘安... 亲爱的读者,你是否曾在游戏中遇到过这样的困扰:一边想要享受游戏带来的乐趣,一边又不想放弃手中的零食或...
安卓系统为什么设系统盘,保障稳... 你有没有想过,为什么安卓系统里会有一个叫做“系统盘”的东西呢?这可不是随便设置的,背后可是有大学问的...
王者怎么加安卓系统的,轻松提升... 你有没有想过,你的手机里那款超酷的王者荣耀,怎么才能让它更好地在你的安卓系统上运行呢?别急,今天就来...
安卓手机系统怎么开热点,共享网... 你有没有想过,当你身处一个没有Wi-Fi信号的地方,而你的安卓手机里却存满了精彩视频和游戏时,是不是...
安卓系统11的平板电脑,性能升... 你有没有发现,最近平板电脑市场又热闹起来了?没错,安卓系统11的新一代平板电脑正在悄悄地走进我们的生...
安卓手机系统创始人,安卓手机系... 你有没有想过,那些陪伴我们每天生活的安卓手机,它们的灵魂是谁赋予的呢?没错,就是那位神秘而又传奇的安...
安卓11系统速度提升,体验再升... 你知道吗?最近安卓系统又升级啦!这次可是直接跳到了安卓11,听说速度提升了不少呢!是不是很心动?那就...
安卓5.1原生系统设置apk,... 你有没有想过,你的安卓手机里那些看似普通的设置,其实隐藏着不少小秘密呢?今天,就让我带你一探究竟,揭...
手机安卓系统玩音游,畅享指尖音... 你有没有发现,现在手机上的游戏种类越来越丰富,尤其是音游,简直让人爱不释手!今天,就让我来给你详细介...
安卓系统与win10,系统融合... 你有没有想过,为什么你的手机里装的是安卓系统,而电脑上却是Windows 10呢?这两种操作系统,就...
苹果系统王者安卓系统可以登吗,... 你有没有想过,为什么苹果系统的手机那么受欢迎,而安卓系统的手机却也能在市场上占有一席之地呢?今天,咱...
安卓系统怎么重制系统还原,安卓... 手机用久了是不是感觉卡得要命,想给它来个大变身?别急,今天就来教你怎么给安卓手机重置系统,让它焕然一...
安卓9系统怎样应用分身,轻松实... 你有没有发现,手机里的APP越来越多,有时候一个APP里还要处理好多任务,分身功能简直就是救星啊!今...
获取安卓系统的ip地址,轻松获... 你有没有想过,你的安卓手机里隐藏着一个神秘的IP地址?没错,就是那个能让你在网络世界里找到自己的小秘...
LG彩电安卓系统升级,畅享智能... 你家的LG彩电是不是最近有点儿“闹别扭”,屏幕上时不时地跳出个升级提示?别急,今天就来给你详细说说这...
阴阳师安卓苹果系统,安卓与苹果... 亲爱的玩家们,你是否曾在深夜里,手握手机,沉浸在阴阳师的神秘世界?今天,就让我带你一起探索这款风靡全...