一、Block简介
Block:即块语法,本质上是匿名函数(没有名称的函数),标准C⾥⾯没有Block,C语⾔的后期扩展版本,加⼊了匿名函数,在C++、JS、Swift等语⾔,有类似语法,叫做闭包。Block语法和函数指针很相似。
因为Block是匿名函数,block变量存放的函数的实现,所以我们可以通过block变量直接调⽤函数。
二、 Block详细
如下图:
(图片摘自http://blog.csdn.net/totogo2010/article/details/7839061)
分析:
由图可以分析 Block的类型:int (^)(int)
-->可以和函数指针做类比
Block变量:myBlock
Block值:^ int (int num){ return 7 * num;}
即:^ 返回值类型 (参数列表){函数体} 其中 返回值类型 可以省略。
Block变量:myBlock
Block值:^ int (int num){ return 7 * num;}
即:^ 返回值类型 (参数列表){函数体} 其中 返回值类型 可以省略。
1. Block基本使用
以下Block实现将字符串转换为数字,并返回。
int (^myBlock)(NSString*);
myBlock = ^(NSString* numStr)
{
return [numStr intValue];
};
调用Block
NSLog(@"%d", myBlock(@"123"));
2. 用 typedef 命名 Block
我们知道,在定义函数指针的时候,直接定义指针很麻烦,而且可读性也不好。所以一般我们都用 typedef 先把函数指针类型定义成新的名称,以方便使用。在定义 Block 时也一样,我们可以借助 typedef 来给 Block 类型定义新的名称。如下:
typedef int(^BlockType)(int x, int y)
经过定义之后,BlockType 就是 int (^)(int x,int y) 类型了。如下定义:
typedef int (^BlockType)(int x,int y);
BlockType myBlock = ^(int x, int y)
{
return x+y;
};
NSLog(@"%d", myBlock(3, 4));
运行结果:7
3. Block实现字符串排序
NSArray *strArray = @[ @"bbb", @"aaa", @"ddd", @"ccc" ];
NSComparator sortedBlock = ^(NSString* s1, NSString* s2)
{
return [s1 compare : s2];
};
NSArray *newArray = [strArray sortedArrayUsingComparator:sortedBlock];
NSLog(@"newArray is : %@", newArray);
运行结果:
分析: NSComparator是什么类型?为什么能直接定义Block变量呢? 按住 command 键, 点击此类型进入 API 查看,如下:
typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);
可以看出,NSComparator就是块类型,即:NSComparisonResult^(id obj1, id obj2) 类型。所以以上的代码没问题。
4.Block 实现对象数组排序
如果一个数组存储的不是字符串,是其他的类对象(如Person类的对象),我们可以这样实现(以下代码按Person对象年龄排序):
Person *per1 = [[Person alloc] init];
per1.age = 20;
Person *per2 = [[Person alloc] init];
per1.age = 18;
Person *per3 = [[Person alloc] init];
per1.age = 22;
NSArray *persons = [NSArray arrayWithObjects:per1, per2, per3, nil];
[persons sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2)
{
Person *per1 = (Person*)obj1;
Person *per2 = (Person*)obj2;
if( per1.age > per2.age )
{
return NSOrderedDescending;
}
else if ( per1.age < per2.age )
{
return NSOrderedAscending;
}
else
{
return NSOrderedSame;
}
}];
当然,你也可以像第三点的代码那样将 Block 抽取出来,即定义一个 Block 变量来存储。
5. Block 访问外部变量
在 Block 中,我们可以直接访问在其 外部的
局部变量,但是不允许修改变量的值, 如下:
a. 访问局部变量
int i = 3;
typedef int (^BlockType)(int x,int y);
BlockType myBlock = ^(int x, int y)
{
return i+x+y;<span style="white-space:pre"> </span>//访问 i
};
b. 修改局部变量, 如下图:
根据错误我们对代码做如下修改:
__block int i = 3;
typedef int (^BlockType)(int x,int y);
BlockType myBlock = ^(int x, int y)
{
i++;
return i+x+y;
};
错误消失!!!
c. 访问并修改全局变量, 见代码:
int i = 3;
int main(int argc, const char * argv[])
{
typedef int (^BlockType)(int x,int y);
BlockType myBlock = ^(int x, int y)
{
i++;
return i+x+y;
};
NSLog(@"%d", myBlock(3, 4));
}
注:全局变量不允许声明为__block类型
6. Block 的递归调用
要想递归调用 Block, 则 Block变量 必须是全局变量或者静态变量。原因有博客说 “
这样在程序启动的时候代码块变量就初始化了”
先看代码:
static int (^sumBlock)(int); //必须为静态变量
sumBlock = ^(int num)
{
int n = num-1;
if( num <= 0 )
{
return 0;
}
return num + sumBlock(n);
};
NSLog(@"sum = %d", sumBlock(5));
我把 static 关键字去掉,然后后进行调试,发现如下图结果:
当程序运行到 Block 语句调用处时, sumBlock 有了如上图所示的内存地址,点击运行下一步
发现,这是后 sumBlock 为 NULL 了, 如果这时候再调用 sumBlock 程序肯定崩溃。而如果将 sumBlock定义成静态变量的话,我们知道静态变量是要等到整个程序运行结束才释放内存的,所以这时可以递归调用。
有个不明白的问题:如果 递归调用 非静态 Block 变量,当运行完 Block 调用处语句之后, sumBlock就会被释放并指向NULL, 而如果非递归调用 非静态 Block 变量,当运行完 Block 调用处语句之后,sumBlock 内存仍然存在(没被释放)。 实在搞不懂到底是为什么?