代码块(Block)的使用

一、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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值