block object是一个C语言级别的语法和运行时特性。它与标准C函数很类似。不同之处在于block包含变量,这些变量绑定于自动的(stack)或者受管理的(heap)内存。因此block维护了一组状态,从而可以在执行过程中来影响行为。
block在OS X10.6及之后版本,iOS4.0及之后版本的GCC和Clang上是可用的。block的运行时是开源的,链接:LLVM's compiler-rt subproject repository。它也被提交给C标准工作小组作为苹果对C语言的扩展:N1370:Apple's Extensions to C
1、block的功能
block是一个匿名的内联代码集合:
1)像函数一样有类型化的参数;
2)有一个可推导的或者已声明的返回类型;
3)可以捕捉它被定义的词法范围的状态;
4)视情况的可以修改被定义词法范围的状态;
5)与同词法范围的block共享修改的潜在能力;
6)即便被定义的词法范围已经被销毁,仍能继续共享和修改这个词法范围定义的状态
block可以被复制,被传递给其它线程做推迟执行。编译器和运行时负责安排block引用的变量被一直保存着,只要block的所有拷贝依然存在。
2、block的用途
block是小的,自包含的代码片段,经常备用来封装会被并发执行的、或者针对集合类型的项目的、以及另一个操作完成的之后回调的一块工作单元。block经常作为矿机方法的参数。block允许访问本地变量,这样你就可以不用定义数据结构来包含执行一个回调操作所需的上下文信息。
3、block基本语法
1)使用^操作符来声明一个block变量,{}内包含具体定义,“;”来表示声明的结束:
int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
return num * multiplier;
};
注意:block可以使用它被定义的作用域内的变量
block被定义之后,可以像使用函数一样使用:
print("%d", myBlock(3));
声明block变量像声明函数指针一样,下面是一些合法的声明:
void (^blockReturningVoidWithVoidArgument)(void);
int (^blockReturningIntWithIntAndCharArguments)(int, char);
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
block也支持可变参数(...),没有参数的block必须在参数列表指定void。你可以转换block引用为任意类型的指针,反之亦然,但是block引用的指针不能用*来解引用。因此block的大小不能再编译的时候计算到。
2)、可以不声明block变量,直接把它作为一个函数参数内联的使用:
[presentingVC presentViewController:presentedVC animated:YES completion:^{
printf("present finished");
}];
3)定义一个block类型:
typedef float (^MyBlockType)(float, float);
MyBlockType myFirstBlock = //...;
MyBlockType mySecondBlock = ^(float a, float b) {
return a - b;
}
4、block和变量
block对象定义体代码中,变量以5种方式对待的,你可以像函数一样引用3种标准类型的变量:
1)全局变量,包括static本地变量
2)全局函数
3)封闭作用于的本地变量和参数
4)在函数级别的__block变量。这一类在block内是可变的,并且如果引用的block被拷贝到heap中时,这一类变量会被保存
5)const
在一个block中有以下规则被应用于变量上:
1)全局变量是可访问的,包括存在于封闭词法范围的static变量
2)传递进block的参数是可访问的
3)词法范围内的栈(stack,非static)本地变量被捕捉为const变量,它们的值在程序中的block表达式的时刻被拿到。嵌套的block中,从最近的词法范围捕捉值。
4)词法范围内声明的__block存储修饰符的本地变量通过引用提供的,所以是可变的
5)block内声明的本地变量跟一般函数内本地变量一样。每一次block的调用为本地变量提供新的值。对于内嵌的block,这些变量又是被看做const或者通过引用来使用的变量
例:本地非静态变量
int x = 123;
void (^printXAndY)(int) = ^(int y) {
//x = x + 1; 如果放开注释,Error
printf("%d %d\n", x, y);
};
printXAndY(456); // prints: 123 456
5、__block存储类型。
__block修饰符是的block中导入的变量是可变的,即就是read & write,它类似于本地变量的register,auto,static存储类型。__block变量对于声明该变量的此法范围和所有的block和同一词法范围声明的或者创建的block拷贝。所以只要引用__block变量的block存在,即便是栈结构销毁,__block变量依然得以幸存。
作为优化,block存储一开始在stack上,就和block一样。但是一旦block被拷贝,例如使用Block_copy(OC中发送一个copy),变量才会被拷贝到heap上。所以__block变量的地址随时间可能会变。
__block变量有两个限制:1)不能是可变长度数组 2)不能是C99中包含可变长度数组的结构体
extern NSInteger CounterGlobal;
static NSInteger CounterStatic;
{
NSInteger localCounter = 42;
__block char localCharacter;
void (^aBlock)(void) = ^(void) {
++CounterGlobal;
++CounterStatic;
CounterGlobal = localCounter; // localCounter fixed at block creation
localCharacter = 'a'; // sets localCharacter in enclosing scope
};
++localCounter; // unseen by the block
localCharacter = 'b';
aBlock(); // execute the block
// localCharacter now 'a'
}
6、对象和block变量
当block被拷贝,block内部使用的对象变量会被强引用。所以:
1)如果你通过引用来访问一个实例变量,self被强引用
2)如果你通过值来访问一个实例变量,这个变量被强引用
dispatch_async(queue, ^{
// instanceVariable is used by reference, a strong reference is made to self
doSomethingWithObject(instanceVariable);
});
id localVariable = instanceVariable;
dispatch_async(queue, ^{
/*
localVariable is used by value, a strong reference is made to localVariable
(and not to self).
*/
doSomethingWithObject(localVariable);
});
7、拷贝block
一般,不需要拷贝一个block,只有在你想当声明block的作用于被销毁之后继续使用这个block才需要拷贝它,拷贝会把block移到heap中:
Block_copy();
Block_release();
这两个必须成对出现的,以防止内存泄漏
8、调试
设置断点,单步调试。可以在GDB会话使用invoke-block:
$ invoke-block myBlock 10 20 //对应block名和参数。
传C字符串有些特殊,要进行转义
$ invoke-block doSomethingWithString "\"this string\"'
本文内容基本是苹果文档Blocks Programming Topics翻译。基本上的知识点应该都知道的。好闲啊!!!