块(Blocks)
《Objective-C程序设计第四版中文翻译》对Blocks介绍得有点少,对block还是糊里糊涂的,真用到的时候有点迷惘。
块是对C语言的一种扩展,优势:能让系统分配给其他处理器或者应用的其他线程执行,通常用于配合GCD写多线程。看起来像函数 ,跟函数相同之处:可以像函数一样给它传值,接收返回值;不同之处:块定义在函数或者方法内部,能访问在函数或者方法范围内块之外的任何变量。访问只能读取变量的值,如果需要改变,要在定义块的时候使用块修改器声明。
块是以插入字符“^”开头为标识的,下面是块的定义和调用。
void (^print_message)(void) =
^(void) {
NSLog(@"Programming is fun.");
}; //定义
print_message();//调用
但是一般你看到的是这样,没有名字的块,只有传入的参数和块内的具体代码。
[reader setCompletionWithBlock:^(NSString *resultAsString) {
webUrl = resultAsString;
[self ReadyToDownload];
[wSelf.navigationController popViewControllerAnimated:YES];
}];
如果在定义变量的时候使用__block , 如 __block int n = 1;则说明这个变量能在代码块内被改变。
但是我还是不懂这个block,那就参考唐巧大牛的博客来做点练习,看能不能自己理解。
1.isa指针,跟对象一样,都有isa指针,用于指向变量或者对象存储的内存地址。
2.flags,用于按 bit 位表示一些 block 的附加信息。
3.reserved,保留变量。
4.invoke,函数指针,指向具体的 block 实现的函数调用地址。
5.descriptor, 表示该 block 的附加描述信息,主要是 size 大小,以及 copy 和 dispose 函数的指针。
6.variables,capture 过来的变量,block 能够访问它外部的局部变量,就是因为将这些变量(或变量的地址)复制到了结构体中。
关于存储位置:在Objective-C语言中,一共有3种类型的block:
(1)_NSConcreteGlobalBlock 全局的静态block,不会访问任何外部变量。
(2)_NSConcreteStackBlock 保存在栈中的block,当函数返回时会被销毁。
(3)_NSConcreteMallocBlock 保存在堆中的block,当引用计数为0时会被销毁。
参考block的例题说明:
Example B
void exampleB_addBlockToArray(NSMutableArray *array) {
char b = 'B';
[array addObject:^{
printf("%cn", b);
}];
}
void exampleB() {
NSMutableArray *array = [NSMutableArray array];
exampleB_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
在MRC的时候,block是_NSConcreteStackBlock类型,被创建保存在栈中,一旦所在函数返回,则会被摧毁。所以exampleB_addBlockToArray(array);运行完之后,存在栈上面的变量b就不存在了。
如果把函数改为
void exampleB_addBlockToArray(NSMutableArray *array) {
[array addObject:^{
printf("cn");
}];
}
这样的话,block是_NSConcreteGlobalBlock,全局的静态block,不会访问任何外部变量。不用考虑内存问题。
Example D
typedef void (^dBlock)();
dBlock exampleD_getBlock() {
char d = 'D';
return ^{
printf("%cn", d);
};
}
void exampleD() {
exampleD_getBlock()();
}
这个也是一样,把block返回,但是里面的变量在没有ARC的情况下是函数返回就被销毁的。这个跟函数一样的,那关于传入参数的问题,应该也是一样的。
总结:这样看来,block像一个能运行代码的对象,里面的变量就像在函数里一样,局部变量会在返回的时候被销毁,需要像Example B和D那样使用block,需要ARC,它把所有对象的放在堆上面,包括block,方便管理。而在block里访问block外的变量的话,只是把值copy进block内,需要加上__block关键字,才能把变量地址传进block,在block内进行修改。