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路径
网友评论