代码块(Block)的使用

来源:互联网 发布:衡水数控编程招聘信息 编辑:程序博客网 时间:2024/06/05 08:15

一、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;} 
即:^ 返回值类型 (参数列表){函数体} 其中 返回值类型 可以省略。

1. Block基本使用


以下Block实现将字符串转换为数字,并返回。
int (^myBlock)(NSString*);    myBlock = ^(NSString* numStr)            {                return [numStr intValue];            };

调用Block
    NSLog(@"%d", myBlock(@"123"));

可以看出,调用Block跟C中调用函数(函数指针)的方法是一样的。myBlock就类似于函数指针。



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类型))
根据错误我们对代码做如下修改:
__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 内存仍然存在(没被释放)。 实在搞不懂到底是为什么?











0 0