Block底层

2021-01-31  本文已影响0人  吕建雄

Block在开发中是一个使用比较高频的技术实现;给开发带来了很多的便捷性,在使用block的过程中,老生常谈的问题就是block对于外部变量的捕获以及容易造成循环引用的问题;

今天分三部分来揭晓下,block对于外部变量的捕获在底层是如何实现的;

一、对于访问了局部变量的block

访问局部变量a的block

对于访问了局部变量的block(栈block),会直接捕获局部变量到block中

底层会生成一个__main_block_impl_0 的结构体和__main_block_func_0函数(__main_block_impl_0此处的0其实与当前代码中block的数量以及第一个block有关系,比如第一个block就是0,如果第二个block就是1...以此类推)

其中:

__main_block_impl_0结构体内部包含:

1、block_impl结构体

2、__main_block_desc_0* Desc,

3、捕获的局部变量(例子中是a)

4、__main_block_func_0函数(构造函数)会包含一个默认参数(这个参数为当前__main_block_impl_0),在函数中就是使用该参数直接来调用其中的变量

访问局部变量的block底层

__main_block_impl_0的构造函数,会将(__main_block_func_0,__main_block_desc_0,捕获的局部变量,flags)作为构造参数,来构造__block_impl对象 ,最终的调用都是使用__block_impl来进行(见上图:__main_block_impl_0函数)

main_block_func_0

二:对于访问了全局变量的block(global block)

访问全局变量b的block

并不捕获变量,而是__main_block_func_1中直接使用

__main_block_impl_1

三、访问使用__block修饰的局部变量

访问__block修饰变量的block

对于使用了__block修饰的局部变量,底层会生成一个__Block_byref_*这样一个结构体(其实也是一个对象,因为有isa),对于__block修饰的变量,直接捕获的是生成的__Block_byref对象

__Block_byref_c结构体

结构体中包含:isa, __forwarding指针,局部变量c,以及__flags,__size

__main_block_impl_2

在__main_block_impl_2中 ,对于__block修饰的变量,直接捕获的是生成的__Block_byref对象

__main_block_func_2

在__main_block_func_2中,对于__block修饰的变量,直接使用的是生成的__Block_byref对象,然后调用该对象的__forwarding来进行修改

实际上对于__block修改的变量,使用的值址传递;

block调用:

block调用

调用block的时候,使用的是__block_impl的FuncPtr调用函数;底层会有一个默认参数,将__block_impl(也即是当前block)传递进去

调用底层实现

代码实现地址

下载代码,然后进入到mian.m路径下,执行如下命令,可直接生成编译后的.cpp文件

clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-14.0.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.3.sdk  main.m

注意:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.3.sdk 需要替换为自己本地的SDK路径

上一篇 下一篇

猜你喜欢

热点阅读