一、捕获自动变量值

首先看一个经典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. Python多进程编程

    转自:Python多进程编程 阅读目录 1. Process 2. Lock 3. Semaphore 4. Event 5. Queue 6. Pipe 7. Pool 序. multiproces ...

  2. URLClassLoader类

    URLClassLoader类 1.URLClassLoader类也是ClassLoader类的实现类,它的功能非常强大,他可以从本地文件系统中获取二进制文本来加载类,也可以从远程主机获取二进制文件来 ...

  3. 20.fastDFS集群java代码测试

    1.工程结构   2.代码内容 FastdfsClientTest.java代码   package cn.itcast.fastdfs.cliennt; import java.io.File; i ...

  4. Linux Shell 批量更换文件名或后缀名

    把下列所有.c的文件名修改为.cc rename .c .cc *.c

  5. ubunt tmux X Error of failed request

    X Error of failed request:  BadName (named color or font does not exist)  Major opcode of failed req ...

  6. android中的HttpURLConnection和HttpClient实现app与pc数据交互

    自学android的这几天很辛苦,但是很满足,因为每当学到一点点知识点都会觉得很开心,觉得今天是特别有意义的,可能这个就是一种莫名的热爱吧. 下面来说说今天学习的HttpURLConnection和H ...

  7. const char* && string && String^ 类型转换

    const char* && string && String^ 类型转换 const char* ---> string const char * cw= &q ...

  8. spark1.5 scala.collection.mutable.WrappedArray$ofRef cannot be cast to ...解决办法

    下面是我在spark user list的求助贴,很快就得到了正确回答,有遇到问题的同学解决不了也可以去上面提问. I can use it under spark1.4.1,but error on ...

  9. cygwin编译环境小记

    [gcc] 预定义宏 编译器通常会根据平台和编译选项的不同,为被编译的程序提供不同的预定义的宏, 例如WIN32 WIN64 LINUX, 例如DEBUG, RELEASE. 1. 在使用gcc/g+ ...

  10. English Literature

    The website links of English Literature,which I wanno recommend to U is based on following. 数据结构 - 知 ...