主要从下面几方面介绍下Block:
1、Block概念
Xcode文档对Block的概念是这么介绍的:
Block对象是一个C级别的语法和运行机制。它允许你写一些函数语句,这些函数语句可以传到API中,可以有选择性地存储,可以用于多线程中,而且还可以引用局部变量和保存对局部变量的存取。……Block可以同时用在C, C++,或者Objective-C,使得程序更有效率和更好的维护。
2、Block的基本用法
Block的用法也是:声明 ——> 创建 ——> 使用
1)声明
先回顾下C的函数指针,比如想用一函数指针指向sum函数:
int sum(int x, int y){
return x + y;
}
则方法为:
int (*p) (int, int); //定义一函数指针p
p = sum; //指针变量p指向函数sum
printf("sum = %d \n", p(2, 3)); //p(2, 3)等同于 sum(2, 3)
而Block的声明和C的函数指针声明很类似,只需把指针变量前的“*”改为“^”。如:
int (^sumBlock)(int, int);
即: 返回类型 (^block名称)(参数列表)= ^ 返回类型(参数列表){ // 代码实现; } ;
声明了一个Block对象sumBlock,sumBlock指向了一个代码块,代码块需要两个int型参数(int , int ),返回值类型是int 。
2)创建和使用
Block表达式以“^”开始,以“;”结束,使用Block方式也与调用函数类似,如:
sumBlock = ^(int a, int b){ //创建变量sumBlock
return a + b; //代码实现
};
NSLog(@"sum = %d", sumBlock(2, 3)); //使用变量sumBlock,实参为 2 和 3
//输出结果:sum = 5
当声明和创建放在任何函数外,则是创建了全局Block。如:
// main.m
#import <Foundation/Foundation.h>
//声明和创建全局Block对象sumBlock
int (^sumBlock)(int, int) = ^(int a, int b){
return a + b;
};
int main(int argc, const char * argv[]) {……}
当Block无参数、无返回值时,则是Block 的最简单形式:
格式: Void (^block名) () = ^ { 代码块; };
使用: block名();
无参数时,参数列表可省略。也可以有参数无返回值,或者无参数有返回值。
3)block的typedef
也是先回顾函数指针的typedef:
typedef int (*FUN) (int, int); //此时FUN代表一个类型,这个类型的变量是一个函数指针
FUN f; // 等同于 int (*p) (int, int);
f = sum;
printf("sum = %d \n", f(2, 3));
而用typedef定义block类型的格式是:
Typedef 返回值类型 (^类型别名)(参数类型列表);
如:
typedef int(^BLOCK)(int, int); //给一类型起别名BLOCK,这个类型是一个有参有返回值的BLOCK类型
int main(int argc, const char * argv[]) {
@autoreleasepool {
BLOCK sumBlock2; //用BLOCK类型定义一个变量sumBlock2
sumBlock2 = ^(int a, int b){
return a + b;
};
NSLog(@"sum = %d", sumBlock2(2, 3));//使用sumBlock2变量
}
return 0;
}
3、Block 与变量
Block在OC中的实现是闭包(closure)。先理解什么是闭包(closure),闭包并不是Objective-C特有的,在C中表现为回调函数(Callbacks),在C++中表现为函数对象(Function objects),在维基百科上(https://en.wikipedia.org/wiki/Closure_(computer_programming))的介绍是:
“In programming languages, closures (also lexical closures or function closures) are a technique for implementing lexically scoped name binding in languages with first-class functions. Operationally, a closure is a record storing a function together with an environment:[1] a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or storage location to which the name was bound when the closure was created ”
即在计算机语言中,闭包(也叫词法闭包(lexical closures)或者函数闭包(function closures))是记录了一个函数和它所在的环境,并将函数的每个自由变量(在封闭域里定义,局部使用的变量)和其值或内存地址关联起来。即使这个函数的调用在变量的作用域之外,也可以通过闭包的引用来访问捕获(captured)到的变量。
如以下程序:
void (^wdd)(); //声明一个无参数无返回值的全局block变量wdd
void callC(){ //定义一个普通函数,在函数中调用block变量
wdd();
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
int c = 8; //定义一个局部变量
NSLog(@"c = %d, c addr: %p", c, &c);
wdd = ^{
NSLog(@"c = %d, c addr: %p", c, &c);//在block中使用变量c
};
callC(); //调用一般函数
}
return 0;
}
程序运行:
已经声明但未初始化的全局block变量wdd是一个结构体,内部指针都为NULL
虽然不执行wdd里面的代码块,但可以看到wdd内部的成员已经被赋予了值
调用普通函数callC(),虽然函数无参数,但在函数中可调用全局block变量wdd
调用wdd,则又回到main函数中创建wdd的代码块
虽然是在callC()函数中调用block变量wdd,而且wdd要用的变量c的作用域只是在main函数而部在callC()函数中(即原始的变量“c”已经脱离了它当初的变量环境),但仍可以用block变量wdd来输出c的值及其地址。而且可以看到在block的内外“c”的值一样而地址不一样,所以这种情况是在block内把变量“c”与它的值关联起来了。
由上面的调试截图也可以看到block变量wdd是有结构体的。
在Block_private.h中对block的结构体定义如下: