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的实质。

最新文章

  1. Spring中AOP(通知)的使用
  2. SQL数据字符串的拆分
  3. sql like in 语句获取以逗号分割的字段内的数据
  4. HBase - Phoenix剖析
  5. 后序/中序--->前序
  6. 使用 Spark 进行微服务的实时性能分析
  7. openssl 摘要和签名验证指令dgst使用详解
  8. 神奇的魔法数字0x61c88647
  9. ios-html-get/post差额,简而言之(MS)CheckST
  10. Head First--设计模式(装饰者模式)
  11. h5学习笔记:vuethink 配置
  12. IOS 使用cocoapods后无法导入头文件问题
  13. APIO 2016
  14. .net解析csv(C#导表工具)
  15. python 第三方库
  16. 使用pdf.js预览实现读取服务器外部文件
  17. 缓慢拒绝服务攻击- slowloris.pl
  18. 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(九)以g711-mulaw为例添加新的编码格式解析支持
  19. leetcode python 005
  20. CentOS6.8 安装 Oracle11.2.0.4

热门文章

  1. Linux下搭建一个nginx+2tomcat负载均衡环境(转)
  2. TCP-IP ---三次握手和四次挥手来啦
  3. 用javaweb写一个注册界面,并将数据保存到后台数据库(全部完成)(课堂测试)
  4. java中的 RSA加密
  5. Oracle数据库自带了decode()函数
  6. Linux设备树学习
  7. 分享Linux CentOS7 VMware 系统目录结构、 ls命令 、文件类型、alias命令——笔记
  8. 「SCOI2010」幸运数字
  9. MyBatis Dao层的编写
  10. Django-路由Routers-SimpleRouter-DefaultRouter使用方法