从源码、ARC、MRC带你理解block的三大类型

首先,在了解block三大类型以前,咱们须要了解一个知识:c++

(舒适提醒:若是个人以前博客你没有看,有些概念你不清楚的话,你可能很难理解,若是前面你都看了,这篇博客你看就像切菜同样简单!)程序员

程序的内存分配

一个由C/C++编译的程序占用的内存分为如下几个部分markdown

栈(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操做方式相似于数据结构中的栈。数据结构

堆(heap): 通常由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式相似于链表。iphone

全局区(静态区static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另外一块区域,程序结束后由系统释放。函数

文字常量区:常量字符串放在这里, 程序结束后由系统释放测试

程序代码区:存放函数体的二进制代码。spa

相信你们对上面的这些内存分配的知识点都已经很是清楚,那咱们就看一下指针

block的类型

block是有3种类型,经过调用class的方法或者isa指针查看具体的类型,最终都是继承NSBlock类型

接下来咱们就看看,block就是一个oc对象,那咱们就能够按照oc的,直接调用class就知道它的类型了code

这里能够看出我前面说的,block就是oc对象,而这个NSGlobalBlock是继承NSBlock的,其余的种类都是继承NSBlock,等说完了,你们能够用这个方法试试其它2种block的的父类.

上面就是block的总共的三种类型:NSGlobalBlock、NSStackBlock、NSMallcBlock,

,这是运行时输出的结果,其实编译的结果可能有点不同,那咱们就把这份文件转成c++文件,这个我以前的博客都提了不少次,此次我就不细说了(xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp)

其实你会发现,编译之后生成的都是NSConcreteStackBlock类型,首先咱们确定以运行时为准,由于编译过程还有个运行,运行就会用runtime动态修改你的东西;还有其实用clang转成c++的代码,有时候并不必定就是最终的代码,其实差异是不大,其实在llvm大概6.0之前都是同样的,如今会生成一个中间文件,还有就是程序运行中也是比较准确,若是有同窗查看源码感受不同,不要很惊讶,这个很正常,能够参考,差异不大.

咱们继续看

而GlobalBlock是在数据区域,MallocBlock是在堆区,StackBlock是在栈区,我这里不说堆、栈、数据区域的区别了,你们这个知识点若是不懂能够去查阅一下它们的区别,否则后面的很难理解

什么样的类型的是GlobalBlock?什么样的类型是MallocBlock?什么样的类型是StackBlock?

先看个人总结,咱们再细细看

auto变量你们清楚吧?auto变量就是它所在的区域执行完毕,自动销毁的变量.若是不知道请看我上一篇博客,介绍的很是清晰.接下来咱们就一个一个看每种类型的block

GlobalBlock类型

请看下面的代码

访问static的局部变量,全局变量a,都不是auto变量,因此上面3种状况都是GlobalBlock,由于GlobalBlock咱们实际中用的很是少,因此这个也没有过多的须要阐述的

StackBlock类型(重点)

按照咱们表格上面说的访问了auto变量的block就是StackBlock,那我看一下下面的代码:

ARC下运行的结果

应该有同窗注意,结果和咱们上面的结论不同,实际上是由于这环境是ARC,ARC下其实帮你作了不少事(这个为何会这样,这个内容仍是有点多,后面的博客,我会细说,你先无论这个为何是这样),因此咱们改为MRC才能看到它的本质(直接把Objective-C Automatic Reference Counting 设置为NO便可),咱们再看下面

MRC下运行的结果

这就是StackBlock类型,下面咱们再深刻看一下这个StackBlock一个特殊状况,以下图

MRC下运行的结果

注意到没有,这个age的值变得很奇怪.这是由于:test()函数的做用域执行结束之后,它的做用域中的栈上面的变量就会销毁,好比里面的block就是StackBlock,全部test()执行完毕之后,block已经被释放了,致使访问内部就会出现混乱的状况.整个block内存被释放了里面的全部都会出现混乱

那上面的怎么解决这种问题呢???

答案很明显,咱们把block栈内存变成堆内存就好了哇!怎么变成堆block,是否是直接栈调用copy就能是堆block了,那咱们再试试:

是否是就很容易解决了!可能有同窗还有疑问,若是把GlobalBlock也来调用copy会是怎么样呢?这个你们能够本身验证,答案仍是:GlobalBlock,若是把MallocBlock也来调用copy会是怎么样呢?这个你们能够本身验证,答案仍是:引用计数加1

还有若是咱们block访问一个对象,一个对象也是auto变量,因此也是同样的,这些我就不验证了,若有疑问能够评论区讨论.

MallocBlock类型

这个上面已经说的很清楚了

相信还有同窗有疑问,刚刚咱们是MRC下测试的,至于ARC为何会是另外一个结果,我后面的博客会说到!

接下来博客我会介绍堆MallocBlock的一些具体状况和使用,来继续探讨block

若是以为我写得对您有所帮助,请关注我,我会持续更新😄

相关文章
相关标签/搜索