Block探究:第三篇(Block_copy)

原文地址:http://www.galloway.me.uk/2013/05/a-look-inside-blocks-episode-3-block-copy/
如原作者发现有侵权行为可责令我在24小时之内删除,前提是你能看到。


因为我一直在忙于我自己的(就是那本大名鼎鼎的《Effective Objective-C 2.0 提高效率的52个tips》)的事,所以一直没顾上这篇文章,距第二篇已经过了好个月,但是我还是将它完成了!

通过我的第一篇文章和第二篇 文章我们已经对block进行了深入了解,这篇文章主要来深入研究block被copy的时候到底做了什么。你可能听过这样一句术语“block一开始都是存储在栈内存上,如果你需要一直使用它那么你必须对block进行copy操作”。但为什么要这样做呢?在copy的时候到底发生了什么?对于block捕获的外部变量又发生了什么?这个问题也困扰了我很久,这篇文章咱们来整一整。


目前为止我们知道的

通过第一篇第二篇我们知道了block在内存中的结构是下面这样的:

block内存布局

通过上一篇文章我们了解到,当block捕获到一个外部非对象类型的变量时,一开始是创建在栈内存上的,因为栈内存是会被系统回收重复使用的,所以如果我们想一直使用这个block的话就需要调用Block_copy()函数,类似于Objective-C中对block发送一条copy消息,因为block可以被看成是一个OC对象,发送copy消息也会去调用Block_copy()函数。

那最好来看看Block_copy()函数做了什么。


Block_copy()

首先我找到了Block.h文件中关于Block_copy的定义:

Block_copy

Block_copy()原来就是一个宏定义,将传入的参数强转为const void *类型然后传给_Block_copy()函数。在runtime.c
文件中有_Block_copy()函数实现的原型:
_Block_copy()

函数内部调用了_Block_copy_internal()函数,_Block_copy_internal()有两个参数,第一个是block自己,第二个是WANTS_ONE,在runtime.c
文件中看看WANTS_ONE的实现过程。我将函数中一些和垃圾回收相关的东西省略了,方便我们阅读:
_Block_copy_internal()

下面来解释一下这个函数做了些啥:
1.如果传入的block参数为空,则函数返回NULL,这是一段防御代码。
2.将参数arg强转为Block_layout *类型,第一篇中我们讲到过Block_layout,他由一个实现函数和一系列元数据共同构成了block的内部结构。
3.如果block中断flags标记位包含BLOCK_NEEDS_FREE那么该block就是一个堆block。这里需要做的是对block的引用计数加一并将block返回。
4.如果block是global block那么直接将block返回。因为global block是个单例。
5.如果能走到第五步,那这个block就是分配在栈上的block,需要将栈block拷贝到堆中,首先调用malloc()分配size大小的内存空间,如果分配失败则返回NULL
6.memmove()函数将分配在栈中的block按位拷贝至刚刚在堆上分配的内存中。按位拷贝可以确保block中的所有元数据都能准确的进行拷贝,例如block的descriptor。
7.这一步是更新block的标志位,第一行确保block的引用计数变为0,后面的注释说这句操作不是必须的,可能因为在这个地方引用计数已经是0了,加上这句代码是为了防御某种情况下引用计数不为0的bug。第二行是设置BLOCK_NEEDS_FREE标记位,这个标记位表明block是一个堆block,当引用计数变为0时,内存会被释放掉,后面紧跟的| 1将block的引用计数置为1。
8.将isa指针置为_NSConcreteMallocBlock,表明这是一个堆block。
9.如果block还有copy helper函数(上一篇末尾提到的__block_descriptor_tmp结构中的__copy_helper_block__destroy_helper_block这两个函数指针)那么也会调用它相关的copy helper函数,他们会对block捕获的对象进行retain和release操作。

这看起来是不是非常的酷,现在你已经知道block是怎么被copy的,但这只是一部分,那它是怎么被release的呢?


Block_release()

Block_release()实际上也是一个宏定义:

Block_release()

Block_copy()函数一样,Block_release()也将传入的参数类型进行强转,然后调用_Block_release。这样做其实是为了方便开发者使用,不用自己再去转换参数类型。

让我们来看看_Block_release()函数的实现(和之前一样,我也对代码进行了优化,删除了垃圾回收相关的部分,方便阅读):

_Block_release()

下面是每行代码的分析:
1.将传入的参数强转为struct Block_layout类型,这也是传入的block的真实类型。然后对传入的参数为NULL的情况作了防御。
2.将block的引用标记位减1,还记得在Block_copy()中对引用计数加了1吗。
3.如果block的引用计数还大于0,那说明还有人对block引用,block现在还不能被释放。
4.如果flags标记位中包含BLOCK_NEEDS_FREE,那么表明这是一个堆block,并且block的引用计数是0,所以这个block应该被释放。这个时候block的dispose helper函数(__copy_helper_block)会被调用。这个函数的作用和copy helper函数(__destroy_helper_block)的作用刚好相反,是用来释放block锁捕获的对象的。当block所捕获的外部对象被释放以后,通过调用_Block_deallocator函数来将block释放,如果你在runtime.c
文件中查找,你会发现该函数的尾部是一个指向free的函数指针,也就是释放掉malloc分配的内存。
5.走到这里说明block是一个global block,所以什么也不用做。
6.如果代码执行到这里了,会发生一些奇怪的事情:因为正在尝试将栈上的block释放掉,所以这行代码是为了提醒开发者的。在程序实际运行过程中,你最好不要看到这个提示。

这就是我要讲的全部了,但block的内容不仅仅只有这些。


下一篇讲啥?

到目前为止我的block系列就告一段落了,其中有的内容参考了我的书(Effective Objective-C 2.0)。这本书中还有更多的如何高效地使用block的内容,如果你对block感兴趣的话这部分内容是你进行深入研究的很好的资料。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容