一、捕获自动变量值

首先看一个经典block面试题:
  1. int val = 10;
  2. void (^blk)(void) = ^{printf("val=%d\n",val);};
  3. val = 2;
  4. blk();

上面这段代码,输出值是:val = 10.而不是2.

block 在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝,然后在 block 块内使用该只读拷贝;换句话说block截获自动变量的瞬时值;或者block捕获的是自动变量的副本。(我更喜欢这么理解:block捕获变量的机制更像是函数按值传递)

由于block捕获了自动变量的瞬时值,所以在执行block语法后,即使改写block中使用的自动变量的值也不会影响block执行时自动变量的值。

所以,上面的面试题的结果是2不是10

__block说明符

    前面讲过block所在函数中的,捕获自动变量。但是不能修改它,不然就是“编译错误”。但是可以改变全局变量、静态变量、全局静态变量。其实这两个特点不难理解:
    ● 不能修改自动变量的值是因为:block捕获的是自动变量的const值,名字一样,不能修改
    ● 可以修改静态变量的值:静态变量属于类的,不是某一个变量。由于block内部不用调用self指针。所以block可以调用。
    解决block不能修改自动变量的值,这一问题的另外一个办法是使用__block修饰符。
  1. __block int val = 10;
  2. void (^blk)(void) = ^{printf("val=%d\n",val);};
  3. val = 2;
  4. blk();

上面的代码,跟第一个代码段相比。只是多了一个__block修饰符。但是输出结果确是2了。

__block 变量的内部实现要复杂许多,__block 变量其实是一个结构体对象,拷贝的是指向该结构体对象的指针。

二、捕获OC对象

对于捕获ObjC对象,不同于基本类型;Block会引起对象的引用计数变化。
  1. @interface MyClass : NSObject {
  2. NSObject* _instanceObj;
  3. }
  4. @end
  5. @implementation MyClass
  6. NSObject* __globalObj = nil;
  7. - (id) init {
  8. if (self = [super init]) {
  9. _instanceObj = [[NSObject alloc] init];
  10. }
  11. return self;
  12. }
  13. - (void) test {
  14. static NSObject* __staticObj = nil;
  15. __globalObj = [[NSObject alloc] init];
  16. __staticObj = [[NSObject alloc] init];
  17. NSObject* localObj = [[NSObject alloc] init];
  18. __block NSObject* blockObj = [[NSObject alloc] init];
  19. typedef void (^MyBlock)(void) ;
  20. MyBlock aBlock = ^{
  21. NSLog(@"%@", __globalObj);
  22. NSLog(@"%@", __staticObj);
  23. NSLog(@"%@", _instanceObj);
  24. NSLog(@"%@", localObj);
  25. NSLog(@"%@", blockObj);
  26. };
  27. aBlock = [[aBlock copy] autorelease];
  28. aBlock();
  29. NSLog(@"%d", [__globalObj retainCount]);
  30. NSLog(@"%d", [__staticObj retainCount]);
  31. NSLog(@"%d", [_instanceObj retainCount]);
  32. NSLog(@"%d", [localObj retainCount]);
  33. NSLog(@"%d", [blockObj retainCount]);
  34. }
  35. @end
  36. int main(int argc, charchar *argv[]) {
  37. @autoreleasepool {
  38. MyClass* obj = [[[MyClass alloc] init] autorelease];
  39. [obj test];
  40. return 0;
  41. }
  42. }

执行结果为1 1 1 2 1。
__globalObj和__staticObj在内存中的位置是确定的,所以Block copy时不会retain对象。
_instanceObj在Block copy时也没有直接retain _instanceObj对象本身,但会retain self。所以在Block中可以直接读写_instanceObj变量。
localObj在Block copy时,系统自动retain对象,增加其引用计数。
blockObj在Block copy时也不会retain。

block捕获自动变量和对象的更多相关文章

  1. 深入研究Block捕获外部变量和__block实现原理

    Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这个新功能“Blocks”.从那开始,Block就出现在iOS和Mac系统各个API中,并被大 ...

  2. 为什么在默认情况下无法修改被block捕获的变量? __block都做了什么?

    默认情况下,block里面的变量,拷贝进去的是变量的值,而不是指向变量的内存的指针.使用__block修饰后的变量,拷贝到block里面的就是指向变量的指针,所以我们就可以修改变量的值.

  3. 静态局部变量、静态全局变量、extern全局变量、自动变量 札记

    静态局部变量 静态局部变量. 从称呼上我们可以看出,静态局部变量首先是一个局部变量,因此其只在定义它的函数内有效,冠以静态的头衔后,其生存期就被延长了,不会随着函数的返回而被撤销.我们可以这样来理解: ...

  4. Python基础一. 简介、变量、对象及引用

    一.Python简介 Python是一门计算机编程语言,它是由荷兰人Guido van Rossum在1989年圣诞节期间为了打发无聊的圣诞节而编写的,作为ABC语言的继承 特性: 面向对象.解释型. ...

  5. 深度剖析Java变量栈&对象堆

    Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...

  6. ios中block访问外部变量的一些注意点

    Block类型是一个C级别的语法和运行机制.它与标准的C函数类似,不同之处在于,它除了有可执行代码以外,它还包含了与堆.栈内存绑定的变量.因此,Block对象包含着一组状态数据,这些数据在程序执行时用 ...

  7. block 对外部引用变量的处理

    MRC 环境 一.静态变量 和 全局变量   在加和不加  __block 都会直接引用变量地址.也就意味着 可以修改变量的值.在没有加__block 参数的情况下. 全局block 和 栈block ...

  8. 学习笔记:Javascript 变量 包装对象

    学习笔记:Javascript 变量 包装对象 如下代码,可以输出字符的长度. var str = "Tony"; str.length; 这时再试试以下代码,返回是 undefi ...

  9. python中的变量与对象

    一. 什么是变量 变量就是以前学习的数学中常见的等式x = 3(x是变量,3是变量值),在编程中,变量不仅可以是数学,还可以是任意数据类型 二. 变量的命名规则 变量名必须是英文大小写.数字和_的组合 ...

随机推荐

  1. SQL MD5加密

    ) 加密结果:

  2. php无限遍历目录-修正版

    最近在能php目录操作,搞了一个目录无限遍历: 使用的函数有: isset()判断某个变量是否定义 chdir() 将当前目录改变为指定的目录. opendir() 打开目录. readdir()读取 ...

  3. jquery总结02-样式和属性

    .attr()  .removeAttr() 设置属性和移除属性,里面可以是属性,属性值 ,只有属性名时只获取第一个蒜素的属性值 .html() .text() .val()  html 获取包括标签 ...

  4. 手机测试pc端网页

    在这个问题上徘徊了 一个钟头了,终于被我找到方法了,就赶紧记下来,以后好查阅!! 主要问题在防火墙,防火墙阻当了80端口,所以怎么用手机访问都是访问不了的.把防火墙关闭就好了! 贴上httpd-vho ...

  5. InnoDB和MyISAM(转)

    两种类型最主要的差别就是Innodb 支持事务处理与外键和行级锁.而MyISAM不支持. 我作为使用MySQL的用户角度出发,Innodb和MyISAM都是比较喜欢的,但是从我目前运维的数据库平台要达 ...

  6. Openvpn 本地密码验证

    1.修改配置文件.(添加下列配置) auth-user-pass-verify /etc/openvpn/checkpsw.sh via-env #开启用户密码脚本 client-cert-not-r ...

  7. 用Js+css3实现图片旋转,缩放,裁剪,滤镜

    还是前端图片的老话题,花了半天时间,东拼西凑,凑出个demo,优点在于代码少,核心代码就6行,目前刚做了旋转,缩放,裁剪,滤镜要js做,网络上也有现成的代码, 但是想做到自定义的滤镜咋办呢?这还要从底 ...

  8. GreenPlum简单性能测试与分析--续

    版权声明:本文由黄辉原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/259 来源:腾云阁 https://www.qclou ...

  9. python核心编程第六章练习6-10

    6-10.字符串.写一个函数,返回一个跟输入字符串相似的字符串,要求字符串的大小写反转,比如,输入“Mr.Ed”,应该返回“mR.eD”作为输出.[答案]代码如下: #!/usr/bin/env py ...

  10. C++ 高级语法学习与总结(代码实例)

     C++11增加了许多的特性,auto就是一个很明显的例子.  还有就是typedid()获取数据变量的类型 看下面简短的代码: atuo: 很像java中的加强for循环..... //获取一个数据 ...