Objective-C 之Block(1)

Block 语法

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

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

与通常函数相比,有两点不一样bash

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

如下为Block语法ide

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

如:ui

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

能够省略返回值类型spa

^ 参数列表 表达式设计

省略返回值类型时,若是表达式中有return语句就使用该返回值的类型,若是表达式中没有人return语句就使用void类型。表达式中含有多个return语句时,全部return的返回值类型必须相同。指针

如:code

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

若是不是用参数,参数列表也能够省略。cdn

^ 表达式

如:

^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 <stdio.h> 
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 <stdio.h>
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]);
};
复制代码
相关文章
相关标签/搜索