Block前言

Block可能已经是被大家讨论的滚瓜烂熟的话题了,却经久不衰。总结这篇文章的起因是在一个开发群里许多iOSer咨询block问题,于是写该系列Block文章系统梳理一下有关Block的知识体系。该系列文章会从Block实质Block类型及内存区域Block截获自动变量Block循环引用问题深入理解Block。

Block实质

在OC语言中,Block从OS X Snow Leopard 和 iOS 4起开始引入,才被众多OC语言使用者周知。OC是基于C语言编写,Block也是基于C语言的扩充,那Block是扩充的何种功能呢?简而言之,Block是带有自动变量(局部变量)的匿名函数。但OC语言又是对C语言的扩展,是OOL,所以Block在OC中的存在绝不仅仅是一段函数。我们可以使用clang命令,查看Block在OC中的实现。(clang -rewrite-objc 源文件名 note:具体如何clang,详情Google )
具体沿着编译后的cpp文件查找,会查找到如下最原始结构

struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
}

可以看到,结构体中有isa指针,Block的实质是与OC中常见objc_object结构体类似,由此可见Block在OC中是作为对象存在
Block实质详解

Block类型及内存区域

在Block实质中,我们探究到了Block的最原始结构,那么Block
有哪几种类型呢,接下来看Block结构体中的isa指针
在编译后的cpp文件中,我们可以看到构造函数中会有如下赋值

__BlockObject__init_block_impl_0(void *fp, struct __BlockObject__init_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }

可以看到 **impl.isa = &_NSConcreteStackBlock**,_NSConcreteStackBlock就代表了Block类型。
类似_NSConcreteStackBlock这样的结构还有以下几种

|类型|查看源|存储区域
_NSConcreteGlobalBlock | .cpp文件/Block.h | 全局变量/静态变量区
_NSConcreteStackBlock | .cpp文件/Block.h|栈区
_NSConcreteMallocBlock |Block_private.h文件|堆区
_NSConcreteAutoBlock |Block_private.h文件|堆区
_NSConcreteFinalizingBlock |Block_private.h文件|堆区
_NSConcreteWeakBlockVariable |Block_private.h文件|堆区

通过编译后cpp文件以及查看runtime中与Block相关文件,找到以上六种类型。我们创建的Block默认赋值的isa指针会有_NSConcreteGlobalBlock_NSConcreteStackBlock 两种类型,,其它四种是在运行期间,编译器根据情况生成。
有关各Block类型、存储区域 以及各类型和存储区域和各种场景的对应详情点击
Block类型及内存区域

Block截获变量

使用Block期间,不可避免要使用一些外部变量或全局变量以及对象等。而Block对于引用的变量或对象有自动截获的能力。对于Global、Stack、Malloc 类型的Block都有自动截获的能力,但对于不同的变量或者是在不同情况下,Block截获变量会出现以下情况:
1.截获变量的值
2.截获变量的指针
3.将变量拷贝到堆区域,并持有变量
对于每种情况的具体分析,详见《Block自动截获变量》