Objective-C中的Block(闭包)

        学习OC有接触到一个新词Block(个人感觉又是一个牛气冲天的词),但不是新的概念,不是新的东西。学过Javascript的小伙伴对闭包应该不陌生吧~学过PHP的应该也不陌生,在PHP5.3版本以后也支持闭包, 也就是OC中所提到的Block。 到底什么是闭包或者block呢?用大白话说就是匿名函数,也就是在函数中可以包含这函数。就是在函数中可以定义匿名函数然后在函数中调用。学习OC中的block之前也小担心一下,Block在OC中属于高级的部分,心里有又有个疑问:学起来难不难?看过Block的部分,感觉Block挺好理解的,用起来也挺顺手的,Block没我想象中的那么难理解。

        废话少说,学习一门新的编程语言是少不了代码量的支持的,所以代码是少不了的。下面就通代码来认识一下OC中的block的使用。

        Block基础部分

        1.Block的声明

            Block的定义和函数的声明差不多,就是把函数名改成(^blockName)即可。下面是block声明的代码。

            有返回值的

int (^sumBlock) (int, int);

            无返回值的 

void (^myBlock)(int, int);

          2.给block块赋值

 

           给声明好的block,赋值。block的值就是个函数体,给block块赋值有两种方式,一个在声明的时候赋值,一个是先声明在赋值。

            先声明再赋值

//代码块的声明
void (^myBlock)(int, int);

//给代码块赋值
myBlock = ^(int a, int b)
{
    //test ++;  //报错
    NSLog(@"main_test = %d", test);

    //blockVar++不报错;
    blockVar ++;
    NSLog(@"blockVar = %d", blockVar);

    int sum = a + b;
    NSLog(@"a + b = %d", sum);
};

            在声明的时候赋值

int (^sumBlock) (int, int) = ^(int a, int b)
{
    int sum = a + b;
    return sum;
};

          3.调用block 

         block的使用和普通函数的使用相同,调用方法如下:

//调用代码块并接收返回值
int sum = sumBlock(20, 30);

         4.把block当做参数传入函数

//把代码块作为函数参数
void blockFunction(int (^myBlock)(int, int))
{
    int sum = myBlock(10,20);
    NSLog(@"fun_sum = %d", sum);
}  

        5.在代码块中使用局部变量和全局变量

            在block中可以和对全局变量进行访问和修改,但对局部变量只可以访问,若想修改的话,我们可以在声明局部变量的时候加上关键字__block

            代码如下:

__block int blockVar = 0;

 

        Block进阶 参考博客:http://www.cnblogs.com/NarutoYq/

        下面的这些内容是参考上面的博客进一步学习的Block的内容,代码是参考这上面的博客自己写的,也就是说下面的东西算是伪原创吧。小伙伴们如果没大看懂下面的东西,请去上面的博客中进一部的了解一下block.

        1.局部变量可变对象和不可变对象在block中的引用

            下面会提供一部代码,这部分代码的功能是定义两个局部变量,一个是可变对象,一个是不可变对象,然后再定义一个Block, 在block中引用两个局部变量。上面提到了在代码块中可以引用局部变量但是不可以更改其值,除非在声明的时候加上__block关键字。

            测试代码如下:

void blockTest1()
{
    //定义两个变量一个是可变的一个是不可变的
    NSString *str1 = @"str1";
    NSMutableString *str2 = [NSMutableString stringWithFormat:@"str2"];

    //初始值

    NSLog(@"两个字符串的初始值和初始地址");
    NSLog(@"str1 = %@,  str1_p = %p", str1, str1);
    NSLog(@"str2 = %@,  str2_p = %p", str2, str2);

    //定义block在block中输出连个变量的值和参数
    void (^myBlock) () = ^()
    {
        NSLog(@"******************************************");
        NSLog(@"在block块中输出局部变量的可变和不可变变量");
        NSLog(@"str1 = %@,  str1_p = %p", str1, str1);
        NSLog(@"str2 = %@,  str2_p = %p", str2, str2);
    };

    //修改前赋值
    str1 = @"str1_update";
    [str2 appendString:@"_update"];
    NSLog(@"******************************************");
    NSLog(@"输出修改后的值和地址");
    NSLog(@"str1 = %@,  str1_p = %p", str1, str1);
    NSLog(@"str2 = %@,  str2_p = %p", str2, str2);

    //调用block
    myBlock();

    NSLog(@"******************************************");
    NSLog(@"调用block后的值和地址");
    NSLog(@"str1 = %@,  str1_p = %p", str1, str1);
    NSLog(@"str2 = %@,  str2_p = %p", str2, str2);

}

        代码说明:给定义的各一个可变和不可变的对象一个初始值,然后在调用代码块的时候修改两个局部变量的值,然后再代码块中显示变量的值。

        运行结果如下:

2014-08-10 13:30:25.710 Memory[1074:303] 两个字符串的初始值和初始地址
2014-08-10 13:30:25.711 Memory[1074:303] str1 = str1,  str1_p = 0x100005ef0
2014-08-10 13:30:25.712 Memory[1074:303] str2 = str2,  str2_p = 0x100204330
2014-08-10 13:30:25.712 Memory[1074:303] ******************************************
2014-08-10 13:30:25.712 Memory[1074:303] 输出修改后的值和地址
2014-08-10 13:30:25.713 Memory[1074:303] str1 = str1_update,  str1_p = 0x100005fd0
2014-08-10 13:30:25.713 Memory[1074:303] str2 = str2_update,  str2_p = 0x100204330
2014-08-10 13:30:25.713 Memory[1074:303] ******************************************
2014-08-10 13:30:25.714 Memory[1074:303] 在block块中输出局部变量的可变和不可变变量
2014-08-10 13:30:25.714 Memory[1074:303] str1 = str1,  str1_p = 0x100005ef0
2014-08-10 13:30:25.714 Memory[1074:303] str2 = str2_update,  str2_p = 0x100204330
2014-08-10 13:30:25.714 Memory[1074:303] ******************************************
2014-08-10 13:30:25.715 Memory[1074:303] 调用block后的值和地址
2014-08-10 13:30:25.715 Memory[1074:303] str1 = str1_update,  str1_p = 0x100005fd0
2014-08-10 13:30:25.715 Memory[1074:303] str2 = str2_update,  str2_p = 0x100204330

         从上面的输出结果我们可以看到,在代码块中输出的不可变对象是原有的值,而不是我们改后的值,地址也是初始的地址。而对于可变对象,值是我们修改后的值,而地址使用原有的地址。如果要想block和不可变局部变量绑定的话,我们要加上_block

        还是引用上面博客中的一段话来做一下总结吧:

  1. 对值类型的修改,如果block初始化后,无法同步到block内部

  2. 对于引用类型的修改,如果block初始化后,修改指针指向,即指向另外一块内存,这样也是无法同步到block内部

  3. 对于引用类型的修改,如果block初始化后,对指针指向的内存进行修改,即NSMutableArray add 、remove操作,这样是可以用同步到block内部,但block内部同样无法修改。

     2.成员变量在block中的使用

    ​    ​成员变量在block中的使用是加上self->a使用的,所以在声明成员变量的时候加不加__block,在成员函数中的代码块中都可以访问修改;

    ​    ​代码走起:

    ​    ​interface:

@interface BlockTest : NSObject
//声明两个成员变量一个用__block 和 不用__block修饰观察其变化
{
    __block NSString *hasBlock;
    NSString *noBlock;
}

-(void)test;

@end

    ​方法的实现:

@implementation BlockTest
-(void)test
{
    //分别给两个成员变量赋初始值
    hasBlock = @"ludashi";
    noBlock = @"ludashi";
    NSLog(@"hasBlock = %@, hasBlock_p = %p", hasBlock, hasBlock);
    NSLog(@" noBlock = %@,  noBlock_p = %p", noBlock, noBlock);

    //定义block
    void (^myBlock)() = ^()
    {
        //修改加__block的成员变量的值
        hasBlock = @"ludashi_update";
        NSLog(@"block中输出的内容");
        NSLog(@"hasBlock = %@, hasBlock_p = %p", hasBlock, hasBlock);
        NSLog(@" noBlock = %@,  noBlock_p = %p", noBlock, noBlock);
    };

    //改变noBlock的值
    noBlock = @"ludashi_update";
    NSLog(@"更新后的值");
    NSLog(@"hasBlock = %@, hasBlock_p = %p", hasBlock, hasBlock);
    NSLog(@" noBlock = %@,  noBlock_p = %p", noBlock, noBlock);

    //调用block
    myBlock();

    //调用block后的值
    NSLog(@"调用myBlock后的值");
    NSLog(@"hasBlock = %@, hasBlock_p = %p", hasBlock, hasBlock);
    NSLog(@" noBlock = %@,  noBlock_p = %p", noBlock, noBlock);
}
@end

   ​输出结果:

2014-08-10 16:32:42.497 Memory[1349:303] hasBlock = ludashi, hasBlock_p = 0x100006188
2014-08-10 16:32:42.499 Memory[1349:303]  noBlock = ludashi,  noBlock_p = 0x100006188
2014-08-10 16:32:42.499 Memory[1349:303] 更新后的值
2014-08-10 16:32:42.500 Memory[1349:303] hasBlock = ludashi, hasBlock_p = 0x100006188
2014-08-10 16:32:42.500 Memory[1349:303]  noBlock = ludashi_update,  noBlock_p = 0x100006828
2014-08-10 16:32:42.500 Memory[1349:303] block中输出的内容
2014-08-10 16:32:42.501 Memory[1349:303] hasBlock = ludashi_update, hasBlock_p = 0x100006828
2014-08-10 16:32:42.501 Memory[1349:303]  noBlock = ludashi_update,  noBlock_p = 0x100006828
2014-08-10 16:32:42.501 Memory[1349:303] 调用myBlock后的值
2014-08-10 16:32:42.502 Memory[1349:303] hasBlock = ludashi_update, hasBlock_p = 0x100006828
2014-08-10 16:32:42.502 Memory[1349:303]  noBlock = ludashi_update,  noBlock_p = 0x100006828

总结:

  1. 对于一个、多个成员变量,不管是否用__block修饰(用不用都没任何影响),block结构体会生成一个成员 :self,并且会引用成员变量所属的对象实例 self。

  2. 对于成员变量的修改都是通过对象self指针引用来实现的。

  3. block内部对于成员变量的访问也是通过block结构体对象的成员self 指针引用来实现的。

时间: 2024-10-10 06:48:27

Objective-C中的Block(闭包)的相关文章

Ruby中使用Block、Proc、lambda实现闭包_ruby专题

闭包(Closure),是指未绑定到任何对象的自由代码,闭包中的代码与任何对象和全局变量无关,只与执行此段代码的上下文相关. 今天我们简要的看一下ruby中的闭包实现. Ruby中的闭包实现有:Block,Proc,Lambada. 首先,我们来看Block. 复制代码 代码如下: ary = [1,2,3,4] ary.collect! do |a|         a*a end ary.each do |a|         puts a end 这段代码,我们使用了Array对象的blo

objective c-认证ios中的block

问题描述 认证ios中的block 在IOS中,可以使用 void*,id 或者 NSObject* 代表对象实例. 有没有方法代表ios Block?谢谢 解决方案 Block其实相当于其它一些高级语言中的"匿名函数".Block的作用是"可以将方法作为方法的参数".在一个方法中如果要传递参数,在定义方法时需要指明方法的参数类型,但有时我们想将一个方法来作为定义的方法中的参数,这时我就要用到Block了.而你问的问题"有没有方法代表ios Block?&

全面解析Objective-C中的block代码块的使用_IOS

1.相关概念 在这篇笔记开始之前,我们需要对以下概念有所了解. 1.1 操作系统中的栈和堆 注:这里所说的堆和栈与数据结构中的堆和栈不是一回事. 我们先来看看一个由C/C++/OBJC编译的程序占用内存分布的结构: 栈区(stack):由系统自动分配,一般存放函数参数值.局部变量的值等.由编译器自动创建与释放.其操作方式类似于数据结构中的栈,即后进先出.先进后出的原则. 例如:在函数中申明一个局部变量int b;系统自动在栈中为b开辟空间. 堆区(heap):一般由程序员申请并指明大小,最终也由

Ruby中的block概念的理解

  Ruby中的block概念的理解:          文中给出了Javascript代码块与Ruby代码块的对比,需要的朋友可以参考下 Ruby 里的 block一般翻译成代码块,block 刚开始看上去有点奇怪,因为很多语言里面没有这样的东西.事实上它还不错. First-class function and Higher-order function First-class function 和 Higher-order function 是函数式编程语言里面的概念,听起来好像很高端的样

Objective-C中的Block回调模式

        在前面的博客中提到了Block的概念和使用方法,个人感觉Block最爽的用法莫过于在回调时用block.感觉比委托回调和目标方法回调用着要顺手,好不好用还得读者亲自用一下才知道.如果 读者之前用过SSH框架的话,看到OC中的Block回调,会感觉非常的亲切,和Java中的接口回调像极了.还是那句话,上些Block的回调代码最为直接.下面的demo是根据笔者的理解,自己设计的一个小小的Block回调的 demo,难免会有不足之处,还望批评指正,尊重原创,转载请注明出处.      

浅谈 Ruby 中的 block, proc, lambda, method object 的区别

当大家在百度中搜索"block proc lambda"的时候,会出来很多关于这几个概念之间区别的介绍,既然搜索结果中已经有了这些介绍,那为什么还要写这篇文章? 相信看过百度搜索结果中排名靠前的几篇文章的同学,都会发现其实这些文章并没有很好的说明他们之间区别是什么,大多只是介绍各自的用法,加上些许的区别,即使个别介绍了一些区别,也不够系统,不够深入. 正是基于上述原因,才酝酿了本文.本文以简单示例的方式,详细的介绍了它们之间的区别.相信您阅读完本文,一定会豁然开朗,并在今后的开发中准确

link中使用了闭包是不是容易内存泄漏?如何避免?

问题描述 link中使用了闭包是不是容易内存泄漏?如何避免? link中使用了闭包是不是容易内存泄漏?如何避免? 解决方案 延长变量的生命周期可能是你不想要的,但是也不是说就是内存泄漏,只是要小心对待这种情况.

ios-IOS中使用block的错误

问题描述 IOS中使用block的错误 在ios应用中一个单例模式: void (^ myBlock)() = ^(){ [self doStuff]; }; 然后就得到这样的错误: use of undeclared identifier self doStuff是单例模式的方法.假如在其他方法中声明这个block,Xcode运行正常. 请各位前辈帮我解释一下原因,谢谢 解决方案 在界面中定义block,然后在@implementation文件中实例化选中的方法: @interface You

ios-iOS中调用block遍历mutableArray后出现错误

问题描述 iOS中调用block遍历mutableArray后出现错误 如图,新手求解. 解决方案 看提示少了]号,很简单的问题 解决方案二: 多贴几张图看看,信息太少了 解决方案三: 看样子是编译报错吧? 检查一下 block 里面的语句有没有写错