一、捕获自动变量值

首先看一个经典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. Python3基础 print 查看一个列表中存储的所有内容

    镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...

  2. CSS3的chapter6

    CSS布局          div标签: 在css布局方式中,div 是这种布局方式的核心对象,我们的页面排版不再依赖于表格, 仅从div的使用上说,做一个简单的布局只需要两样东西:div 与 cs ...

  3. 断言(ASSERT)的用法

    我一直以为assert仅仅是个报错函数,事实上,它居然是个宏,并且作用并非“报错”. 在经过对其进行一定了解之后,对其作用及用法有了一定的了解,assert()的用法像是一种“契约式编程”,在我的理解 ...

  4. aws在线技术峰会笔记-主会场

    容器服务:Elastic container service IoT可以采用无服务器架构.

  5. Claims Identity

    using System;using System.Collections.Generic;using System.Linq;using System.Security.Claims;using S ...

  6. Less入门到上手——前端开发利器<二>深入了解

    接着昨天的继续... ... 4.嵌套: HTML部分 <table> <tr> <th colspan="3">测试列表标题</th&g ...

  7. react native get started run 模拟机报错解决

    参照 http://reactnative.cn/docs/0.30/getting-started.html#content 1)当执行 react-native run-android 这个环节的 ...

  8. Head First 设计模式--2 观察者模式 解耦

    观察者模式定义了对象之间一对多的依赖,这样依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新. 假如有这么一个项目,监控每天的天气状况WeatherData(温度,湿度,气压).有布告板 ...

  9. Python内置的字符串处理函数整理

    Python内置的字符串处理函数整理 作者: 字体:[增加 减小] 类型:转载 时间:2013-01-29我要评论 Python内置的字符串处理函数整理,收集常用的Python 内置的各种字符串处理 ...

  10. 习题-第1章了解ASP.NET MVC

    一.选择题 1.ASP.NET MVC自2007年首次公布预览以来,作为(    )的替代品,普及度已明显提高,现在很多大型Web应用程序都是使用这一技术构建的. A.ASP    B.ASP.NET ...