block定义
- 格式:
返回类型 (^block名字) (参数列表);
- 同C语言的定义函数指针一样,C语言的函数指针定义格式:
返回类型 (*指针名字) (参数列表);
block赋值
名字 = ^{xxx};
- 如同普通的变量赋值一样类型、分号都需要‘一一对应’,如同
int a = 5;
,^
标志右边的代码段是block类型。
block调用
block名字 (参数列表);
例(无参):
//定义
void (^valenti) ();
//赋值
valenti = ^{
NSLog(@"VaLenTi is MEEEEE!");
};
//调用
valenti();
例(有参):
//定义
void (^valenti) (NSString* name);
//赋值
valenti = ^(NSString* name){
NSLog(@"VaLenTi is %@!",name);
};
//调用
valenti(@"ME");
例(有参有返回值):
//定义
NSInteger (^sum)(NSInteger value1, NSInteger value2);
//赋值
sum = ^(NSInteger value1, NSInteger value2){
return value1 + value2;
};
//调用
NSInteger result = sum(1,1);
typedef与block
如若有这样的需求:定义四个block实现两个参数的加减乘除,他们的代码是如下这样的:
//加
NSInteger (^add)(NSInteger value1, NSInteger value2);
add = ^(NSInteger value1, NSInteger value2){
return value1 + value2;
};
NSInteger result = add(1,1);
//减
NSInteger (^sub)(NSInteger value1, NSInteger value2);
sub = ^(NSInteger value1, NSInteger value2){
return value1 - value2;
};
NSInteger result2 = sub(1,1);
//乘
NSInteger (^mul)(NSInteger value1, NSInteger value2);
mul = ^(NSInteger value1, NSInteger value2){
return value1 * value2;
};
NSInteger result3 = mul(1,1);
//除
NSInteger (^div)(NSInteger value1, NSInteger value2);
div = ^(NSInteger value1, NSInteger value2){
return value1 / value2;
};
NSInteger result4 = div(1,1);
可见,除了block的名字和操作不同以外,其余的结构都是相同的,那么,相同的部分即可用typedef起别名的形式“抽取”出来。格式如下:
typedef 返回值类型 (^block名字) (参数列表)
- 同C语言的函数指针别名一样,名称即代表别名。
上述代码可改为:
//1.在类扩展处定义别名
typedef NSInteger (^calculate) (NSInteger value1,NSInteger value2);
//2.在实现中定义对应功能的block代码并赋值
//加
calculate add = ^(NSInteger value1, NSInteger value2){
return value1 + value2;
};
NSInteger result = add(1,1);
//减
calculate sub = ^(NSInteger value1, NSInteger value2){
return value1 - value2;
};
NSInteger result2 = sub(1,1);
//乘
calculate mul = ^(NSInteger value1, NSInteger value2){
return value1 * value2;
};
NSInteger result3 = mul(1,1);
//除
calculate div = ^(NSInteger value1, NSInteger value2){
return value1 / value2;
};
NSInteger result4 = div(1,1);
注意事项
- block可以访问外部变量,例:
int a = 10;
void (^block)() = ^{
NSLog(@"%zd",a);
}
block中可以定义和外界同名的变量,在block内部外部存在同名变量的情况下,block访问的变量是内部变量-“就近原则”。
默认情况下,不可以在block内部修改外部的变量,1中block内部是不可以对a进行赋值的,因为block中的a和外部的a本质上并不是同一个a,block访问的外部变量会将外部的变量拷贝一份到堆内存中,验证:
int a = 5;
NSLog(@"%p",&a);
void (^block)()= ^{
NSLog(@"%p",&a);
};
block();
结果是:
0x7fff53f42a2c
0x7fb601475220
- 如果想在block中修改外界变量的值,必须在外界变量前面加上__block,在内部修改了变量的值会直接影响外部的值,但是内部外部的变量依然不是同一个,他们的内存地址依然不同。
__block int a = 5;
NSLog(@"%p",&a);
void (^block)() = ^{
a = 10;
NSLog(@"%p",&a);
};
block();
结果是:
0x7fff5100ea88
0x7fe931629858
那么,加上__block就可以的本质原因就是传值方式的原因。
- 把未加__ block修饰的那段代码的ViewController.m的代码编译成C++代码,如图:
画框代码即是核心部分,如下:
int a = 5;
void (*block)() = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, a));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
历尽艰难险阻终于找到a
的影子,其中__ViewController__viewDidLoad_block_impl_0
是一个结构体,在这里传了三个参数:(void *)__ViewController__viewDidLoad_block_func_0
、&__ViewController__viewDidLoad_block_desc_0_DATA
和a
,不难看出,这里是直接将a作为参数传递,也就是值传递,既然是值传递,修改里面的值对外部的a自然是无效的。
那么用__block修饰代码的cpp文件,就如下图:
画框部分是核心代码,如下:
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 5};
void (*block)() = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
仅仅加了一个修饰词,代码就有了不小的变动,这里的结构体传了四个参数,a
在第三个位置,并且a
也有了修饰:(__Block_byref_a_0 *)&a
,不难看出,这里的&标志着这里是指针传递,既然是指针传递,修改里面的值肯定会影响到外部的那个变量。