博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Objective-C 之Block(1)
阅读量:6700 次
发布时间:2019-06-25

本文共 5190 字,大约阅读时间需要 17 分钟。

Block 语法

Blocks是C语言的扩种功能,是带有自动变量(局部变量)的匿名函数。

^void (int event){	printf("buttonId: %d event = %d\n", i , event);}复制代码

与一般函数相比,有两点不同

  1. 没有函数名(匿名函数)
  2. 带有"^"(插入记号)

以下为Block语法

^ 返回值类型 参数列表 表达式

如:

^int (int count){
return count+1;}复制代码

可以省略返回值类型

^ 参数列表 表达式

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

如:

^(int count){ return count+1;}复制代码

如果不是用参数,参数列表也可以省略。

^ 表达式

如:

^void (void){ printf("Blocks\n");}复制代码

可省略为:

^{ printf("Blocks\n");}复制代码

Block 类型变量

在定义C语言函数时,就可以将所定义函数的地址赋值给函数指针类型的变量中。

int func(int count){	return count+1;}int (*funcptr) (int) = &func;复制代码

这样一来,函数func的地址就能赋值给函数指针类型变量funcptr中了。

注:引用知乎fan wang关于指针的回答,解释一下指针类型变量原理

作者:fan wang 链接:https://www.zhihu.com/question/31022750/answer/50629732 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

一图胜千言1. 声明变量:C语言声明一个变量时,编译器在内存中留出一个唯一的地址单元来存储变量,如下图,变量var初始化为100,编译器将地址为1004的内存单元留给变量,并将地址1004和该变量的名称关联起来。

2.创建指针:变量var的地址是1004,是一个数字,地址的这个数字可以用另一个变量来保存它,假设这个变量为p,此时变量p未被初始化,系统为它分配了空间,但值还不确定,如下图所示。
3.初始化指针,将变量var的地址存储到变量p中,初始化后(p=&var),p指向var,称为一个指向var的指针。指针是一个变量,它存储了另一个变量的地址。
4.声明指针:typename *p 其中typename指的是var的变量类型,可以是 short ,char ,float,因为每个类型占用的内存字节不同,short占2个字节,char占1个字节,float占4个字节,指针的值等于它指向变量的第一个字节的地址 。*是间接运算符,说明p是指针变量,与非指针变量区别开来。 5.*p和var指的是var的内容;p和&var指的是var的地址
6.既然指针*p的值等于var,p的值等于&var,为什么要多发明这一个指针符号增加记忆量呢。指针主要的功能有两个:避免副本和共享数据。指针的重要功能是函数之间传递参数。 talk is cheap, show me the code! 假设用c语言设计一个游戏,控制人物向前走的函数为 go_forward(),这个函数接收游戏人物的坐标(int x,int y) 两个变量,对这两个变量进行加减操作。

#include 
void go_forward(int position_x,int position_y){ position_x=position_x+1; position_y=position_y+1;}int main(){ int x=0; int y=0; go_forward(x,y); printf("当前坐标为:%d,%d \n",x,y); return 0;}复制代码

你希望执行go_forward()函数后x,y坐标都+1,输出为(1,1),但是结果还是(0,0)原因为C语言调用函数的方式是按值传递参数,以x参数为例,刚开始main函数中有一个x的局部变量,值为0,当计算机调用go_forward()函数时,它将变量x的值复制给了参数position_x,这只是一个赋值过程将变量x赋值给变量position_x,相当于 position_x=x 命令,而这个命令,x的值是不发生变化的,结果如下图所示,x的值仍为0,position_x的值变为1。

解决方法,传递指针,用指针告诉go_forward()函数参数x的值的地址,go_forward()函数就能修改对应地址中的内容。所以用指针的主要原因是让函数共享存储器,一个函数可以修改另一个函数创建的数据,只要提供数据在内存中的地址,修改代码如下。

#include 
void go_foward(int *position_x,int*position_y){ *position_x=*position_x+1; *position_y=*position_y+1;}int main(){ int x=0; int y=0; go_forward(&x,&y); printf("当前坐标为:%d,%d \n",x,y); return 0;}复制代码

运行结果:当前坐标为:1,1


同样地,在Block语法下,可以将Block语法赋值给生命为Block类型的变量中。即源代码中一旦使用Block语法就相当于生成了可赋值给Block类型变量的“值”。

声明Block类型变量的示例如下:

int (^blk)(int);复制代码

与前面的使用函数指针的源代码对比可知,声明Block类型变量仅仅是将声明函数指针类型变量的"*"变为"^"。该Block类型变量与一般C语言变量完全相同,可用作:

  • 自动变量(局部变量)
  • 函数参数
  • 静态变量
  • 静态全局变量
  • 全局变量
int (^blk)(int) = ^(int count){	return count+1;}复制代码

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

int (^bkl1)(int) = blk;int (^blk2)(int);blk2 = blk1;复制代码

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

void func(int (^block)(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 (^block)(int)){}复制代码

可以变为:

void func(blk_t blk){}复制代码
int (^func()(int)){	return ^(int count){		return count+1;		}}复制代码

可以变为:

blk_t func(){	return ^(int count){		return count+1;		}}复制代码

另外,将赋值给Block类型变量中的Block方法像C语言通常的函数调用那样使用,这种方法与使用函数指针类型变量调用函数的方法几乎完全相同。 例:变量funcptr为函数指针类型是,想下面这样调用函数指针类型变量。

int result = (*funcptr) (10);复制代码

变量blk为Block 类型的情况下,这样调用Block类型变量:

int result = blk(10);复制代码

在函数参数中使用Block类型变量并在函数中执行Block的例子如下:

int func(blk_t blk, int rate){	return blk(rate);}复制代码

在OC中也可以:

- (int) methodUsingBlock:(blk_t) blk rate:(int)rate{	return blk(rate);}复制代码

Block类型变量可完全像C语言变量一样使用,因此也可以使用指向Block类型变量的指针,即Block的指针类型变量。

typedef int(^blk_t)(int);blk_t blk = ^(int count){ return count+1;};blk_t *blkptr = &blk;(*blkptr)(10);复制代码

截获自动变量值

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

运行结果为:

val = 10复制代码

在该源代码中,Block语法的表达式使用的是它之前声明的自动变量fmt和val。Block中,Block表达式截获所使用的自动变量的值,即保存该自动变量的瞬间值。因为Block表达式保存了自动变量的值,所以在执行Block语法后,即使改写Block中使用的自动变量的值也不会影响Block执行时自动变量的值。

__block 说明符

由于Block表达式截获了所使用的自动变量的值,如果在Block中尝试修改自动变量的值会编译错误:

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

编译之后:

Variable is not assignable (missing __block type specifier)复制代码

若想在Block语法的表达式中将值赋给在Block语法外声明的自动变量,需要在该自动变量上附加__block说明符。

上述代码修改为:

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

运行结果为:

val = 1复制代码

使用附有__block说明符的自动变量可在Block中赋值,该变量成为__block变量。

截获的自动变量

如果截获Objective-C对象,调用变更该对象的方法如下:

id array = [[NSMutableArray alloc] init];void (^block)(void)=^{	id obj = [[NSObject alloc] init];	[array addObject : obj];};复制代码

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

另外,在使用C语言数组时必须小心使用其指针。

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

编译后报错:

Cannot refer to declaration with an array type inside block复制代码

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

const char *text = "hello";void (^blk)(void) = ^{	printf("%c\n",text[2]);};复制代码
你可能感兴趣的文章
FusionCharts简单教程(一)---建立第一个FusionCharts图形
查看>>
sql中实现split()功能
查看>>
ZOJ 2562 More Divisors(高合成数)
查看>>
[原]Android打包之跨平台打包
查看>>
C++的try_catch异常
查看>>
(转)思考:矩阵及变换,以及矩阵在DirectX和OpenGL中的运用问题:左乘/右乘,行优先/列优先,......
查看>>
HDU1452:Happy 2004(求因子和+分解质因子+逆元)上一题的简单版
查看>>
获取MSSQL Server中的相关信息(视图、存储过程、触发器、表)
查看>>
信号处理过程中的几种常见傅里叶相关的变换
查看>>
[原创] Win7全自动精简批处理_绝对原创,绝对给力_感谢无忧给了我一年的潜水...
查看>>
2017 年热门编程语言排行榜,你的语言上榜没?
查看>>
poi 合并单元格、设置边框
查看>>
Hibernate延迟加载与opensessioninviewFilter
查看>>
Atitit 图像处理 调用opencv 通过java api attilax总结
查看>>
服务管理--systemctl命令
查看>>
SQLServer 维护脚本分享(09)相关文件读取
查看>>
Winscp开源的SSH|SFTP
查看>>
闪回之 回收站、Flashback Drop (table、index、trigger等)
查看>>
office-word去掉效验红色的波浪线
查看>>
Struts2之Action与配置文件
查看>>