*注:解释内容主要参考《Objective-C 高级编程》
1.介绍:
Blocks是C语言的扩充功能:带有自动变量(局部变量)的匿名函数。
Blocks的语法:^ 返回值类型 参数列表 表达式
例如:
^int (int c){return c+1;}其中,返回值类型 是可以省略的,会按return类型返回,如果不需要参数,那么参数列表 也可以省略,如:^{PRintf("hello");}2.Block类型变量
int (^blk)(int) = ^(int c){return c+1;}; int x = blk(5); NSLog(@"x = %d",x);Block值^(int c){return c+1;} 被赋值给了变量blk中,blk即为Block变量。利用typedef定义Block别名:
typedef int (^blk_1)(int); blk_1 blkk = ^(int x){return x+3;}; NSLog(@"x = %d",blkk(3));意指用blk_1来代替int ^(int);这种块,之后调用赋值什么的会更舒服。3.截获自动变量值
int x = 1; void (^blk)(void) = ^{ NSLog(@"zhi = %d",x); }; x = 6; blk();以上代码输出结果 zhi = 1;可见在Block截获的是自动变量x的值,之后改变x,并不会对块的执行结果有影响。并且我们如果想在块中改变x的值是无法通过编译的。如果我们想在块中改变x的值,需要用__block说明符4.__block说明符
__block int x = 1; void (^blk)(void) = ^{ x = 6; }; blk(); NSLog(@"x = %d",x);输出结果为 x = 6
用__block修饰x后,便可以在块中改变截取的自动变量x,那么:__block int x = 1; void (^blk)(void) = ^{ NSLog(@"x = %d",x); }; x = 7; blk();输出结果为 x = 7这里思考,如果我们不用__block,截取OC对象,会怎么样:
NSString *age = @"26"; NSMutableDictionary *dic =[[NSMutableDictionary alloc]init]; [dic setObject:age forKey:@"age"]; void (^blk)(void) = ^{ NSString *a = [dic objectForKey:@"age"]; NSLog(@"age = %@",a); }; NSString *age2 = [dic objectForKey:@"age"]; age2 = @"18"; [dic removeObjectForKey:@"age"]; [dic setObject:age2 forKey:@"age"]; blk();输出结果为 age = 18
这里我们初始化了一个dic,里面有一个key为@"age"的NSString,初始值为26,在块中输出字典中字符串的值,然后在块执行前,改变dic中字符串。由此我们联想:
NSString *age = @"26"; NSMutableDictionary *dic =[[NSMutableDictionary alloc]init]; [dic setObject:age forKey:@"age"]; void (^blk)(void) = ^{ NSString *age2 = [dic objectForKey:@"age"]; age2 = @"18"; [dic removeObjectForKey:@"age"]; [dic setObject:age2 forKey:@"age"]; }; blk(); NSString *a = [dic objectForKey:@"age"]; NSLog(@"age = %@",a);输出 age = 18
所以在块中也能改变dic的值。这里我们分析,我们用块截取的是NSMutableDIcitionary的实例指针,不能对其赋值,但是可以使用它,即可以向其中添加改变对象。
*这里我们要注意:
const char text[] = "HelloWorld"; const char *text1 = "HelloWorld"; void (^blk)(void) = ^{ printf("%c",text1[1]); }; blk();我们可以使用截取*text1,但是text[]不可以,因为截取自动变量的方法并没有实现对C语言数组的接获,应该用指针方法解决。4.Blocks的实现那么这种截取自动变量以及__block修饰变量,背后是怎样实现的呢,本文简单介绍一下有关截取变量这里,至于Blocks实现的详细解析放在后续文章中。
我们利用clang的指令转变一下我们的文件,打开终端,利用 clang -rewrite-objc ‘文件名’ 对我们的文件进行操作,我们的文件内容如下:
int main(){ int i = 10; int (^blk)(void) = ^{ int x = i+1; return x; }; int jieguo = blk(); return 0;}声明一个简单的块,截获了自动变量 int i,我们用clang指令操作它得到.cpp文件:struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int i; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};//块中函数static int __main_block_func_0(struct __main_block_impl_0 *__cself) { int i = __cself->i; // bound by copy int x = i+1; return x;}由于转化后代码太多,就只截取了一部分。可以看到结构体构造函数初始化 i(_i) , 让i = _i,这是一个copy的过程,所以外部改变自动变量的值,不会影响到块中的值。如果使用__block修饰的话:
struct __Block_byref_i_0 { void *__isa;__Block_byref_i_0 *__forwarding; int __flags; int __size; int i;};struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_i_0 *i; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static int __main_block_func_0(struct __main_block_impl_0 *__cself, int x) { __Block_byref_i_0 *i = __cself->i; // bound by ref int y = x + (i->__forwarding->i); return y;}可以看到,在构造函数中,i(_i->__forwarding) , 这里用一个结构体__Block_byref_i_0,以指针的方式去保存自动变量,保存了变量的地址,那么后续改变自动变量,块中的值也会相应改变。有关块循环引用的问题,及各种使用会在后面文章中介绍。
新闻热点
疑难解答