iOS开发Block底层原理 - 捕获机制,循环引用梳理

Block底层原理 - 捕获机制,循环引用梳理

    前段时间通过学习小马哥视频,对block有了深刻的理解和学习,对此总结一下,加深印象。

1.1 捕获机制

在Object-C中,Block的本质是一个封装了函数调用函数调用环境的OC对象(内部有isa指针):

    函数调用:block内部将要执行的代码

    函数调用环境:传进block中的参数和在内部调用时需要访问的外部变量(捕获机制原因)

但是:绝大部分情况Block创建后,并不会立即调用,而是在其它函数或者合适的时机调用,而block外部的变量有可能已经被销毁;

这时候例如局部变量a已经被销毁,a被销毁后,调用block时内部需要访问a的值,而打印出来a的值为5,证明:Block内部将外部变量捕获进内部,在调用block函数时使用


捕获机制原因

1.1.1 局部变量与全局变量

auto变量:自动变量,离开作用域就销毁。

auto,static局部变量、全局变量

对于auto和static类型的变量,全局变量的捕获,可以通过OC代码转化为C/C++代码查看,使用终端cd到当前文件夹下,输入:

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件

注意:1.输出的文件必须以.cpp后缀命名 2. 必须cd到当前文件夹或者指定文件夹,不然生成的.cpp文件在用户目录下(或者通过在finder中全局搜索可以找到)

由上图代码生成的C++代码

由生成的c++代码可知:

摘自小码哥课件

1、auto变量a会直接将值捕获进去,用一个新的a成员变量接收并保存,所以捕获以后,block外部的a与内部的a为两个不同的值,互不干扰,修改一个的值不会影响另一个的值;

2、static变量捕获的是*b,捕获的是指针,内部与外部是同一个指针,修改一个另一个也会改变;

3、全局变量不会被block捕获,而是在调用函数的时候直接访问。

由此很好的解释了一个面试经常会问到的问题和答案:

block捕获机制经典问题

ps:通过加断点可以打印出外部的a和block内部的a地址值不一样,b、c内外的地址值一样。

1.1.2 Blcok的类型

block分为3中类型:__NSGlobalBlock__、__NSStackBlock__、__NSMallocBlock__,最终都继承自NSBlock ->NSObject。

1、__NSGlobalBlock__:没有访问外部auto变量;调用copy后什么也不做;

2、__NSStackBlock__:访问了外部的auto变量;调用copy后,block从栈区复制到堆区,类型变成__NSMallocBlock__;

3、__NSMallocBlock__:__NSStackBlock__调用copy后的类型;调用copy函数后引用计数+1

NSMallocBlock和NSStackBlock

在ARC环境下,编译器会自动将block执行copy操作复制到堆上,例如:(block需要被保留后面调用,因为栈上的的block出作用域就会被销毁)(1)block作为函数的返回值时;(2)GCD中的block;(3)将block赋值给__strong强指针时;(4)block作为Cocoa API中方法名含有usingBlock的方法参数时。

arc下栈上的block会被copy到堆上(摘自小码哥课件)

1.1.3 block内部实现及调用c/c++分析

block对person对象捕获代码

1.1.3.1  main函数底层c/c++代码实现:

main函数底层c/c++代码
main函数简化后

对main函数简化后,总结:

1、创建person对象是对YYPerson类对象发送alloc和init两条消息,给age赋值是对person对象发送setAge消息;

2、block赋值实际上就是__main_block_impl_0结构体;

3、调用block,通过block找到FuncPtr(函数调用的地址),执行__main_block_func_0

1.1.3.2 block impl、func底层实现及简化

block底层c/c++实现
blcok简化及内部结构体(构造函数)

c++构造函数:对于block的内部实现,__main_block_impl_0,初始化使用到了c++结构体及构造函数,查阅后了解到其语法及实现:

c++构造函数

1.1.3.3 blcok赋值以及调用

main函数以及block impl、func联系

1、block赋值实际上就是__main_block_impl_0结构体,以及传入结构体的参数:__mian_block_func_0,person对象,__main_block_desc_0函数描述等;

2、调用block,通过block找到FuncPtr(函数调用的地址),执行流程:__main_block_impl_0 -> impl -> FuncPtr -> __main_block_func_0 ->调用。

3、FuncPtr:__main_block_impl_0赋值时传入的第一个参数是__main_block_func_0,而在构造函数赋值时第一个参数是void  *fp,即说明__func_block_func_0就是fp,而后fp被赋值给了(__block_impl)impl(block对象)的FuncPtr;

1.1.3.4 对象类型的auto变量的捕获

peson对象的捕获流程

1、当block存储在栈上:block随时会被销毁,所以不会对auto对象进行强引用;

2、如果block被拷贝到堆上:会调用block内部的copy函数,copy函数内部会调用_Block_object_assign函数,_block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用;

3、当堆上的block被移除时:调用block内部的dispose函数,dispose函数会调用_block_object_dispose函数,_block_object_dispose函数会自动释放引用的auto对象

1.2 循环引用的产生和解决

block捕获self示例

1、oc方法在生成c++代码以后可以发现,oc方法都有两个隐式参数:self和_cmd,在调用self.a等方法的时候,使用的就是这个隐式的参数self;

2、所以在oc方法中,self是一个参数,也就是一个局部变量,由oc的捕获机制,局部变量self会被捕获到block内部;

3、self被捕获到block内部,会对self产生强引用,而self对block也是强引用,就会造成循环引用

如何解决循环引用问题,需要打破双方之间的强引用关系,改用弱引用:

如何解决循环引用(摘自小码哥课件)

4、使用__block修饰解决循环引用问题:必须要调用block。

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

推荐阅读更多精彩内容