格而知之16:我所理解的Block(3)
23、在前文中的例子中,Block结构体里的isa指针还没有详细讲解,这个指针都被置向了_NSConcreteStackBlock,它标识了Block的类型。
其实除了_NSConcreteStackBlock这个类型外,Block还有其他的类型,这些类型总共有3种:
(1)、_NSConcreteStackBlock
(2)、_NSConcreteMallocBlock
(3)、_NSConcreteGlobalBlock
24、这3种类型分别表示了Block对象存储的区域,如下图:

25、编译器的模式也会影响到Block对象的存储,同一个Block对象在ARC模式下和在MRC模式下可能会存储在不同的区域。接下来,就使用代码来研究一下在ARC模式下和在MRC模式下,全局Block、普通Block、截获变量的Block和截获可变变量的Block分别是怎么存储的。
26、可以通过打印出Block对象来确定它存储的位置,这3种类型的Block对象打印出来的类分别是:
(1)、__NSStackBlock__
(2)、__NSMallocBlock__
(3)、__NSGlobalBlock__
普通全局Block
27、首先在ARC模式下,编写一个全局普通Block,并分别打印出持有这个Block对象的变量,和这个变量的复制值:

可以发现,两种情况下打印出来的Block对象都是__NSGlobalBlock__类的,说明它们都存储在.data区。
28、试一下在MRC模式下执行相同的代码:

发现效果相同。说明,普通全局Block在ARC模式下和MRC模式下的存储区域是一样的,都是.data区。
截获变量的全局Block
29、全局Block能截获的变量,只能是全局变量(不可能获取到局部变量)。而全局变量在Block中本身就是可变的了,所以“截获变量的全局Block”就相当于“截获可变全局变量的全局Block”。
30、编写以下代码,在ARC模式下执行:

在MRC模式下也执行相同代码:

从执行结果可以看出,截获了变量的全局Block存储区域和普通全局Block是一样的。这说明了,全局Block都是__NSGlobalBlock__类的,存储在.data区。
普通Block
31、对于普通Block,编写以下代码来查看它的存储区域,在ARC模式下分别打印出持有普通Block的变量、持有普通Block的变量的复制值、普通Block对象和普通Block对象的复制值:

再在MRC模式下也执行相同代码:

从打印结果可以看出:不论是在ARC模式下还是在MRC模式下,不截获变量的普通Block都是当作__NSGlobalBlock__类对象处理的,存储在.data区。
还有一点需要注意:前文在使用clang命令转换普通Block的时候,Block对象的isa指针是指向_NSConcreteStackBlock的,说明普通Block本质上是__NSStackBlock__类的;而在此处通过打印可知,因为没有截获变量,编译器实际上就将普通Block当做__NSGlobalBlock__类来处理了。
截获变量的Block
32、上文演示的几种Block在不同编译器模式下存储区域并没有差别,接下来的Block就开始有差别了。
编写以下代码,在ARC模式下分别打印出持有截获变量Block的变量、持有截获变量Block的变量的复制值、截获变量的Block对象和截获变量的Block对象的复制值:

然后在MRC模式下执行相同代码,查看结果:

比较两种模式下的打印结果,可以发现:
(1)、在MRC模式下,截获变量的Block对象本身是存储在栈上的,即使通过持有这个Block对象的变量来访问它,仍然能顺利访问到存储在栈上的Block对象。只有主动调用copy方法,才能将Block对象从栈上复制到堆上;
(2)、在ARC模式下,截获变量的Block对象本身也是存储在栈上的,只是当使用持有这个Block对象的变量来访问它时,这个Block对象就会被从栈上复制到堆上,这样变量访问到的就是存储在堆上的Block对象了。如果主动调用copy方法,Block对象也会被赋值在堆上;
(3)、比较两种模式下的区别,可以发现在使用变量访问Block对象的情况下,在不同编译器模式下Block对象的存储区域是不同的。所以才会有ARC模式下没有_NSConcreteStackBlock这种说法。
截获可变变量的Block
33、再来看一看最后一种Block,编写以下代码,在ARC模式下分别打印出持有截获可变变量Block的变量、持有截获可变变量Block的变量的复制值、截获可变变量的Block对象和截获可变变量的Block对象的复制值:

然后在MRC模式下执行相同代码,查看结果:

可以发现,情况和截获变量的Block一样,都是在使用变量访问Block对象的情况下,在不同编译器模式下Block对象的存储区域不同。那么可以得出以下结论:
(1)、在不同编译器模式下,截获变量的Block本身都是存储在栈上的;
(2)、使用变量访问这种Block时,MRC模式下能访问到栈上的对象,ARC模式下访问到的是被复制到堆上的对象。
格而知之16:我所理解的Block(3)的更多相关文章
- 格而知之16:我所理解的Block(2)
11.那么Block到底是怎么实现的呢?试一试通过将Block 的代码转换成普通C语言代码来查看它的实现过程. 要将OC代码转换成C语言代码,可以使用clang编译的一个命令: 通过这个命令能把指定文 ...
- [转载]你需要知道的 16 个 Linux 服务器监控命令
转载自: 你需要知道的 16 个 Linux 服务器监控命令 如果你想知道你的服务器正在做干什么,你就需要了解一些基本的命令,一旦你精通了这些命令,那你就是一个 专业的 Linux 系统管理员. 有些 ...
- 格而知之3:Core Data的基本使用
最近准备做一个随手笔记类的app给自己用,考虑到从未使用过Core Data,就决定用Core Data来做数据存储.在网上参考了一些Core Data的资料后,用一天的时间写了这个demo,主要测试 ...
- 格而知之6:我所理解的Runtime(1)
基本简介 1.根据官方文档,OC有一个特性:它会尽可能把一些决定从编译时和链接时推迟到运行时才处理,所以这门语言需要的就不只是一个编译器,它还需要一个runtime系统来处理那些已经被编译过的代码. ...
- 格而知之15:我所理解的Block(1)
1.Block 本质上是一个struct结构体,在这个结构体中,最重要的成员是一个函数(当然除函数外还有其他重要的成员). 2.在开始解析Block之前,首先来回顾一下Block的格式.Block相关 ...
- 格而知之8:我所理解的Runtime(3)
关联对象 14.使用Category对类进行拓展的时候,只能添加方法,而不适合添加属性(可以添加属性,也可以正常使用get方法和set方法,只是不会自动生成以下划线开头命名的成员变量). 可以通过关联 ...
- 格而知之7:我所理解的Runtime(2)
消息发送(Messaging) 8.以上便是runtime相关的一些数据结构,接下来我们回看一开始的疑问: objc_msgSend()函数在执行的过程中是如何找到对应的类,找到对应的方法实现的呢? ...
- 格而知之5:我所理解的Run Loop
1.什么是Run Loop? (1).Run Loop是线程的一项基础配备,它的主要作用是来让某一条线程在有任务的时候工作.没有任务的时候休眠. (2).线程和 Run Loop 之间的关系是一一对应 ...
- Python程序员鲜为人知但你应该知道的16个问题(转)
add by zhj: 没找到原文出处,只能找到转载的,文中说有17个坑,其实是16个 全文如下 这篇文章主要介绍了Python程序员代码编写时应该避免的16个“坑”,也可以说成Python程序员代码 ...
随机推荐
- CSS3实现三角形
很多时候我们用到三角形这个效果: 我们可以用CSS3实现这个效果,怎去做呢?先阐述一下原理,我们定义一个空的div,设置这个div宽高为0,给这个div加上一个100px边框(这里是方便观察),得到的 ...
- JMeter简单的性能测试实例
JMeter基础之——一个简单的性能测试 我们了解了jmeter的一此主要元件,那么这些元件如何使用到性能测试中呢.这一节创建一个简单的测试计划来使用这些元件.该计划对应的测试需求. 1)测试目标网站 ...
- [RxJS] Adding Conditional Logic with Filter
Often you only want values to proceed through your stream if they meet certain criteria, just as if ...
- C++11 lambda 表达式
C++11 新增了很多特性,lambda 表达式是其中之一,如果你想了解的 C++11 完整特性,建议去这里,这里,这里,还有这里看看.本文作为 5 月的最后一篇博客,将介绍 C++11 的 lamb ...
- 把自己的程序打成jar包,让别人调用
我们写程序的时候往往需要把自己的程序打包成jar包,给第三方调用.Eclipse让我们非常方便的可以导出jar包.但是当程序里需要用到res里的资源时,往往就会出现问题.因为统自动生成的R类如果被打 ...
- Android-IM架构设计
###1. 架构总览 ###2. 模块介绍 ####2.1 协议封装与任务流程 #####1) 协议与任务的封装 a. 协议有协议头(协议头因为格式相同,被抽象出来)和协议体组成,协议有两类:请求协议 ...
- Sybase安装后的配置工作
1.配置数据库参数 配置sybase数据库使用的最大内存 用isql命令行实用工具登录sybase数据库服务器,其中的servername是$SYBASE/interfaces文件中配置的sybase ...
- CentOS 6.8编译安装httpd2.2.31+MySQL5.6.31+PHP5.3.27
CentOS 6.8编译安装httpd2.2.31+MySQL5.6.31+PHP5.3.27 说明: 操作系统:CentOS 6.8 32位 准备篇: 一.系统约定 软件源代码包存放位 ...
- 在用EF新增对象存贮至数据库时汪报错,但数据库里没有新增数据
大致的问题是这样的: 原来一直用存贮数据的方法是用的是:DBContext.AddToXXXX(),这个方法.在写代码的时候看到VS提示这个方法已失效,推荐使用DBContext.XXXX.AddOb ...
- 整理的sql sever一些数据库查询面试题
当然,我整理的只是一些常见的面试题,具体数据库就不给了,相信大家能看懂!!! --2列出EMPLOYEES表中各部门的部门号,最高工资,最低工资 select Max(salary) as '最高工资 ...