美文网首页
iOS中block的作用域问题以及__block的分析

iOS中block的作用域问题以及__block的分析

作者: 天地不仁以万物为刍狗 | 来源:发表于2016-05-10 20:38 被阅读802次

             用block可以定义任意的代码片段,将其像对象一样传入另一个方法;它是c级别的语法,和C语言中的函数指针非常相似。在C语言中,函数指针和block写法分别如下

    int (* func1)(int a, int b);//c函数指针

    返回类型 | 函数名 | 参数

    int (^backValue)(int num);//block

    返回类型 | 方法名  |  参数

           从表面来看两者唯一的区别就在于*和^。通常来说,block都是一些简短代码片段的封装,适用做工作单元,通常用来做并发任务、遍历以及回调。

    block对象也是对象,实例化方法如下

             int (^backValue)(int) = ^(int num) {

                 return num+1;

            };

            block需要注意的一个特性就是"Variable Capturing",直译过来就是捕捉变量。block会将“捕捉”到的变量复制一份,然后对复制品进行操作,这是非常重要的一点。对于以下代码来说打印结果是b-->2,这是因为在block作用域内会复制a,然后对复制的a进行操作,作用域外的a就不会产生变化,所以,在a++;之后再调用backValue(1),结果是2

           int a = 1;

          int (^backValue)(int) = ^(int num) {

          return num+a;

         };

         a += 1;

        int b = backValue(1);

        NSLog(@"b-->%d", b);

    如果希望block作用域内可以修改外边的变量,可以使用__block(注意是两个下划线)来修饰int a。这样以来结果就会是b-->3。

    示例:

    输出结果:(三次输出的内存地址是完全相同的,注意代码执行顺序)

         0x7fff5c5e0bc0

        100--0x7fff5c5e0bc0

         aa = 100--0x7fff5c5e0bc0

            结论:当用__block来修饰变量int aa = 10 的时候,在block内部调用变量aa的时候,使用的是在相同内存空间中修改过的变量aa的值,即变量aa的值从10修改为了100。那么此时打印aa得到的结果就是修改过的值100,且地址还是开始声明aa的时候所开辟的内存空间的地址

    输出结果:

        0x7fff58b61bc8

     100--0x7fff58b61bc8

      aa = 10--0x7fff58b61bb8

    结论:对比上面使用了__block修饰变量的输出结果可以看到,当未使用__block来修饰变量aa的时候,在block内部使用的变量aa的内存地址和声明的  int aa = 10开辟的内存空间的地址是不同的。重点到了,那么是什么原因导致的这个问题呢????

          原因是:当变量aa未使用__block修饰的时候,block会将“捕捉”到的变量复制一份,然后对复制品进行操作。也就是说预编译的时候,block已经把变量aa复制了一份出来(可以理解为重新声明了一个同名的变量,此时的两个变量在内存中的地址是不同的),当最后一句代码执行了block调用的时候,block内部使用的是自己复制(本质上是深拷贝)出来的一个变量

    刚才的例子中a是一个基本类型的变量,如果block外是一个oc对象的话,结果就又不一样了,比如下边代码,结果就是"block作用域内赋值"

    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 300, 300, 40)];

    label.text = @"block作用域外赋值";

    void (^backValue)(NSString *) = ^(NSString *str) {

    label.text = str;

    };

    backValue(@"block作用域内赋值");

    [self.view addSubview:label];

    这是因为UILabel *label;定义了一个对象指针,在block作用域内复制的是label这个指针,而不是对象,复制之后的指针仍然指向原来的对象,所以对label.text进行操作是可以修改原对象的,但是作用域内如果想下边这样写就不行了,会得到提示Variable is not assignable。

    label = [[UILabel alloc] init];

    相关文章

      网友评论

          本文标题:iOS中block的作用域问题以及__block的分析

          本文链接:https://www.haomeiwen.com/subject/aopyrttx.html