Block存储区域

首先,需要引入三个名词:
● _NSConcretStackBlock
● _NSConcretGlobalBlock
● _NSConcretMallocBlock
正如它们名字显示得一样,表明了block的三种存储方式:栈、全局、堆。block对象中的isa的值就是上面其中一个,下面开始说明哪种block存储在栈、堆、全局。

------------【要点1】:全局block------------

● 定义在函数外面的block是global类型的
● 定义在函数内部的block,但是没有捕获任何自动变量,那么它也是全局的。比如下面这样的代码
  1. typedef int (^blk_t)(int);
  2. for(...){
  3. blk_t blk = ^(int count) {return count;};
  4. }
虽然,这个block在循环内,但是blk的地址总是不变的。说明这个block在全局段。注:针对没有捕获自动变量的block来说,虽然用clang的rewrite-objc转化后的代码中仍显示_NSConcretStackBlock,但是实际上不是这样的。下图可以证明该类型的block是全局的。Xcode5.1.1调试结果
无论ARC与否,上图控制台输出是 <__NSGlobalBlock__: 0x10000f280>,这可能是编译器的优化,本人推测,没有求证。所以用clang的-rewrite-objc是不准确的。

------------【要点2】:栈block--------------

这种情况,在非ARC下是无法编译的,在ARC下可以编译
  1. typedef void (^block_t)() ;
  2. -(block_t)returnBlock{
  3. __block int add=10;
  4. return ^{printf("add=%d\n",++add);};
  5. }
这是因为:block捕获了栈上的add自动变量,此时add已经变成了一个结构体,而block中拥有这个结构体的指针。即如果返回block的话就是返回局部变量的指针。而这一点恰是编译器已经断定了。在ARC下可以编译过,是因为ARC使用了autorelease了。
再说一个场景:
  1. -(block_t)returnBlock{
  2. __block int add=10;
  3. block_t blk_h =^{printf("add=%d\n",++add);};
  4. return blk_h;
  5. }
  6. block_t bb = [self returnBlock];
  7. bb();

这段代码,只是使用了一个自动block变量,可以编过,但是造成程序崩溃了。
如果在返回block的时候加上copy,可以输出正确的数值11

------------【要点3】:堆上的block ----------------

有时候我们需要调用block 的copy函数,将block拷贝到堆上。看下面的代码:
  1. -(id) getBlockArray{
  2. int val =10;
  3. return [NSArray arrayWithObjects:
  4. ^{NSLog(@"blk0:%d",val);},
  5. ^{NSLog(@"blk1:%d",val);},nil];
  6. }
  7. id obj = getBlockArray();
  8. typedef void (^blk_t)(void);
  9. blk_t blk = (blk_t){obj objectAtIndex:0};
  10. blk();
这段代码在最后一行blk()会异常,因为数组中的block是栈上的。因为val是栈上的。解决办法就是调用copy方法。这种场景,ARC也不会为你添加copy,因为ARC不确定,采取了保守的措施:不添加copy。所以ARC下也是会异常退出。

---------------------【要点4】copy的使用-----------------------------------

不管block配置在何处,用copy方法复制都不会引起任何问题。
在ARC环境下,如果不确定是否要copy block尽管copy即可。ARC会打扫战场。
【注意】:
● 在栈上调用copy那么复制到堆上
● 在全局block调用copy什么也不做
● 在堆上调用block 引用计数增加

------------------【对《Objective-C 高级编程》的挑战】-----------------------

    笔者用Xcode 5.1.1 iOS sdk 7.1 编译发现:并非《Objective-C 高级编程》这本书中描述的那样,-rewrite-objc这个命令转化的中间代码,并不可靠。
    block在ARC和非ARC有巨大差别:下面笔者用两种方式来验证:
1.通过Xcode调试结果,附图
2.通过变量的地址 int val肯定是在栈上的,我保存了val的地址,看看block调用前后是否变化。输出一致说明是栈上,不一致说明是堆上。
【第一个方法】,最直观.代码如下,很简单。block捕获了变量val(无论val是否是__block)
  1. -(void) stackOrHeap{
  2. __block int val =10;
  3. blkt1 s= ^{
  4. return ++val;};
  5. s();
  6. blkt1 h = [s copy];
  7. h();
  8. }

在非ARC和ARC下,调试结果如下:

可以看到非ARC下一个是stack一个是Malloc。ARC下都是Malloc
【第二个方法】,声明一个局部变量指针。通过指针来看
  1. typedef int (^blkt1)(void) ;
  2. -(void) stackOrHeap{
  3. __block int val =10;
  4. intint *valPtr = &val;//使用int的指针,来检测block到底在栈上,还是堆上
  5. blkt1 s= ^{
  6. NSLog(@"val_block = %d",++val);
  7. return val;};
  8. s();
  9. NSLog(@"valPointer = %d",*valPtr);
  10. }
在ARC下>>>>>>>>>>>该block被会直接生成到堆上了。看log: val_block = 11 valPointer = 10
在非ARC下>>>>>>>>>该block还是在栈上的。 看log:val_block = 11 valPointer = 11

调用copy之后的结果呢:

  1. -(void) stackOrHeap{
  2. __block int val =10;
  3. intint *valPtr = &val;//使用int的指针,来检测block到底在栈上,还是堆上
  4. blkt1 s= ^{
  5. NSLog(@"val_block = %d",++val);
  6. return val;};
  7. blkt1 h = [s copy];
  8. h();
  9. NSLog(@"valPointer = %d",*valPtr);
  10. }

在ARC下>>>>>>>>>>>无效果。 val_block = 11 valPointer = 10
在非ARC下>>>>>>>>>确实复制到堆上了。 val_block = 11 valPointer = 10

----------------【总结】-----------------

用这个表格来表示。捕获变量包括仅仅读取变量和__block这种写变量,两种方式(其实结果是一样的)
在ARC下:似乎已经没有栈上的block了,要么是全局的,要么是堆上的
在非ARC下:存在这栈、全局、堆这三种形式。

Block存储区域的更多相关文章

  1. block存储区域——怎样验证block在栈上,还是堆上

    Block存储区域 首先,须要引入三个名词: ● _NSConcretStackBlock ● _NSConcretGlobalBlock ● _NSConcretMallocBlock 正如它们名字 ...

  2. 局部变量存储区域静态变量存储区域static变量存储区域

    局部变量存储区域静态变量存储区域static变量存储区域 常见的存储区域可分为: 1.栈 由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区.里面的变量通常是局部变量.函数参数等. 2.堆 ...

  3. C/C++程序内存的各种变量存储区域和各个区域详解

    转自 https://blog.csdn.net/jirryzhang/article/details/79518408 C语言在内存中一共分为如下几个区域,分别是: 1. 内存栈区: 存放局部变量名 ...

  4. C语言5种存储区域

    C语言5种存储区域 转发至:http://www.mamicode.com/info-detail-927635.html 系统为了管理内存 把内存划分了几个区域 1> 栈区 栈区之中的数据在栈 ...

  5. 存储区域网络(Storage Area Network,简称SAN)

    存储区域网络(Storage Area Network,简称SAN)采用网状通道(Fibre Channel ,简称FC,区别与Fiber Channel光纤通道)技术,通过FC交换机连接存储阵列和服 ...

  6. 浅谈个人对存储区域网络SAN的理解

    存储区域网络SAN,是一种通过将网络存储设备和服务器连接起来的网络,提供计算机和存储设备间的数据传输.其中,SAN是独立于服务器系统之外的,拥有近乎无限的存储能力,通过利用光纤作为传输媒介,实现了高速 ...

  7. OC 中的block存储位置

    以下所有在ARC情况下: 一.block块的存储位置(block块入口地址):可能存放在2个地方:代码区.堆区(程序分5个区,还有常量区.全局区和栈区,对于MRC情况下代码还可能存在栈区.关于分区详细 ...

  8. C语言 内存四大存储区域

    #include<stdio.h> #include<stdlib.h> //程序代码指令,define定义的常量---代码区(只读) //全局(关键)变量/常量,静态(关键) ...

  9. jvm在存储区域

    当区域执行的数据  JVM存储器的管理分为几个时间之后的数据区的实施:程序计数器.JavaVM栈.本地方法栈.Java堆.方法区(包括常量池的实现).   程序计数器 较小的内存空间,能够看作是当前线 ...

随机推荐

  1. ibatis mybatis sql语句配置 符号不兼容 大于号 小于号<!CDATA[ ]>

    ibatis mybatis sql语句配置 符号不兼容 大于号 小于号<!CDATA[ ]> 因为这个是xml格式的,所以不允许出现类似">"这样的字符,但是都 ...

  2. 使用redis-dump进行Redis数据库合并

    前言 最近处理数据时,涉及到跨服务器访问的问题,我有两个Redis服务器分别在不同的机器上,给数据维护带来了诸多不便,于是便研究了下如何将两个Redis中的数据合并到一处. 从网站搜了一些工具,找到了 ...

  3. HTML的基本认识

    就目前学的HTML,感受最深的就是很多标签.HTML不怎么需要逻辑,只需记忆大量标签.不懂的可以参照W3C的文档.里面有很多学习的东西,很受用. 关于CSS基础: 基本选择器: 1.标签选择器    ...

  4. iOS - Xcode 配置

    1.Xcode 配置 1.1 OS X 1)main 文件注释修改路径: /Applications(应用程序) ▸ Xcode.app ▸ Contents ▸ Developer ▸ Librar ...

  5. C语言细节——献给入门者(一)

    C语言细节——献给入门者(一) 主题  输入输出需要注意的细节 首先我们要知道大致有scanf(),printf(),getchar(),putchar(),gets(),puts()这几种输入方式. ...

  6. K-邻近算法

    K-邻近算法 采用测量不同特征值之间的距离来进行分类 Ad:精度高,对异常值不敏感,无数据输入假定 Na:计算复杂度高,空间复杂度高 KNN原理 存在样本集,每个数据都存在标签,输入无标签的新数据后, ...

  7. applicationContext.xml简单笔记

    applicationContext.xml简单笔记 <?xml version="1.0" encoding="UTF-8"?> <bean ...

  8. iOS开发 火星坐标转百度坐标

    CLLocationCoordinate2D coor = CLLocationCoordinate2DMake(latitude, longitude);//原始坐标 //转换 google地图.s ...

  9. 关于如何来构造一个String类

    今天帮着一位大二的学弟写了一个String的类,后来一想这个技术点,也许不是什么难点,但是还是简单的记录一些吧! 为那些还在路上爬行的行者,剖析一些基本的实现..... 内容写的过于简单,没有涉及到其 ...

  10. chrome浏览器遇到的异常

    昨天写了一个二进制输出图片的方法,发现在chrome浏览器里面出了异常: (failed) net::ERR_INCOMPLETE_CHUNKED_ENCODING   代码是这样写的: //直接输出 ...