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

549 查看

111194012-3b6a5c9d5edb1aae

前言

Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这个新功能“Blocks”。从那开始,Block就出现在iOS和Mac系统各个API中,并被大家广泛使用。一句话来形容Blocks,带有自动变量(局部变量)的匿名函数。

Block在OC中的实现如下:

121194012-1739b7e85e46b4db

从结构图中很容易看到isa,所以OC处理Block是按照对象来处理的。在iOS中,isa常见的就是_NSConcreteStackBlock,_NSConcreteMallocBlock,_NSConcreteGlobalBlock这3种(另外只在GC环境下还有3种使用的_NSConcreteFinalizingBlock,_NSConcreteAutoBlock,_NSConcreteWeakBlockVariable,本文暂不谈论这3种,有兴趣的看看官方文档)

以上介绍是Block的简要实现,接下来我们来仔细研究一下Block的捕获外部变量的特性以及__block的实现原理。

研究工具:clang
为了研究编译器的实现原理,我们需要使用 clang 命令。clang 命令可以将 Objetive-C 的源码改写成 C / C++ 语言的,借此可以研究 block 中各个特性的源码实现方式。该命令是

目录

  • 1.Block捕获外部变量实质
  • 2.Block的copy和release
  • 3.Block中__block实现原理

一.Block捕获外部变量实质

131194012-b7046feffe189bfa

拿起我们的Block一起来捕捉外部变量吧。

说到外部变量,我们要先说一下C语言中变量有哪几种。一般可以分为一下5种:

  • 自动变量
  • 函数参数
  • 静态变量
  • 静态全局变量
  • 全局变量

研究Block的捕获外部变量就要除去函数参数这一项,下面一一根据这4种变量类型的捕获情况进行分析。

我们先根据这4种类型

  • 自动变量
  • 静态变量
  • 静态全局变量
  • 全局变量

写出Block测试代码。

141194012-cba895ef7fe45179

这里很快就出现了一个错误,提示说自动变量没有加__block,由于__block有点复杂,我们先实验静态变量,静态全局变量,全局变量这3类。测试代码如下:

运行结果

这里就有2点需要弄清楚了
1.为什么在Block里面不加__bolck不允许更改变量?
2.为什么自动变量的值没有增加,而其他几个变量的值是增加的?自动变量是什么状态下被block捕获进去的?

为了弄清楚这2点,我们用clang转换一下源码出来分析分析。

(main.m代码行37行,文件大小832bype, 经过clang转换成main.cpp以后,代码行数飙升至104810行,文件大小也变成了3.1MB)

源码如下