iOS 技术篇:从使用到了解block底层原理 (二)
block实质
序言
上篇文章中主要通过简单的demo展示了block的使用场景,本篇将基于上篇文章iOS 技术篇:从使用到了解block底层原理 (一)进一步了解block底层的实现原理。
block作为一种"带有自动变量值的匿名函数",在实际编译时,我们无法转换成我们能够理解的源代码,但clang(LLVM编译器)具有转化为我们可读源代码的功能。终端输入如下命令行,可获取.cpp文件。
clang -rewrite-objc 源代码文件名
在main.m中实现如下
int main(int argc, const char * argv[]) {
@autoreleasepool {
void(^myBlock)(void)=^{
NSLog(@"testtest");
};
myBlock();
}
return 0;
}
通过clang我们获取到main.cpp 文件,打开文件我们可以看到大约10w行代码,当然我们不用从头到尾细细分析每块的功能。我们只需要看最后几行然后步步分析就可以。
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void(*myBlock)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
return 0;
}
在main函数入口这里我们可以看到我们原实现block被转化为了另一种方式展示。不要着急看不到,从这里开始一步步分析。
void(*myBlock)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
通过全局搜索可以看到__main_block_impl_0的内部结构是:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
这里包含了两个结构体block_impl和main_block_desc_0,
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
//__main_block_impl_0 结构声明
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
不难看出: 1.main_block_impl_0就是block的一个C++的实现,_0代表是main中的第几个block,里面包含了保留值reserved和大小Block_size。 2.block_impl 就是block的内部实现。其中包含了 isa指针、flags标示、reserved保留值和funcPtr定义类型 (ps:_NSConcreteStackBlock相当于class_t结构体实例 想知道可以自己了解下)。
从这里我们可以大概看出main_block_impl_0就是传了两个值main_block_func_0和&main_block_desc_0_DATA。一个是func结构体,另一个是desc指针。 以上就是初始化main_block_impl_0结构体成员的源代码。 接下来,将我们原来看到不太容易理解的__main_block_impl_0通过以上分析可以将
void(*myBlock)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
转化为:
struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
struct __main_block_impl_0*myBlock = &tmp;
这样就容易理解了,该源代码将main_block_impl_0 结构体类型的自动变量,即栈上生成的main_block_impl_0结构体实例的指针,赋值给__main_block_impl_0结构体指针类型的变量myBlock。 而myBlock();
的底层实现如下:
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
转化之后就是
(*myBlock->impl.FuncPtr)(myBlock);
这就是简单的函数指针调用函数。
可以理解为:有block语法转化的__main_block_func_0函数的指针被赋值成员变量FuncPtr中。
到此也就了解了block的实质。
原文地址:https://www.cnblogs.com/lovemargin/p/11945320.html
- 【DeveMobile实例】利用Mobile Detect 制作单独移动端页面项目
- 互联网+智能物流高峰论坛举行运的易现场签约完成战略布局
- Quartz.net通过配置文件来完成作业调度
- JavaScript 基础(一)
- 我也来说说.net开源
- 是时候对员工进行网络安全培训了:黑客正将目标瞄准打印机
- 进度条ProgressBar
- Microsoft Visual Studio International Pack
- 柯洁5冠在手“食言”再战AI:我已看开 输赢无所谓
- JGulp: 利用Gulp 配置的前端项目自动化工作流
- 微软Enterprise Library 4.0将支持依赖注入
- 时钟AnalogClock与DigitalClock
- 细数那些在2017年被黑客滥用的系统管理工具和协议
- Compass: 在你的应用中集成搜索功能
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- css用target实现选择卡
- 002-Django数据库及后台admin配置
- ES6 ... 展开&收集运算符
- The inherit, initial, and unset values
- protobuf DNK下的编译
- python3 连接 zookeeper
- 20191010
- Winform中封装DevExpress的MarqueeProgressBarComtrol实现弹窗式进度条效果
- lnmp配置
- 基于SpringBoot的Swagger2快速入门
- Angular中上传图片到分布式文件服务器FastDFS上
- Vue Element使用第三库icon图标
- Python获取房价信息和导出EXCEL
- C#编程之C#基础(六)
- spring JdbcTemplate在spring的ioc中使用