面试相关iOS Developer

Block学习笔记2-Block实质

2017-02-18  本文已影响299人  Shirley_y

参考书籍:《Effective Objective-C 2.0》 《Objective-C高级编程 iOS与OS X多线程和内存管理》
根据书中所说,Block是“带有自动变量值的匿名函数”,下面算是我的读书笔记,附上由clang编译出来的过程代码来理解Block的实质。
首先介绍下clang,Clang是一个[C语言]、[Objective-C]、C++语言的轻量级编译器。---百度百科

1.相关clang命令

可能用到的关于clang的指令如下:
cd到main目录下,执行clang -rewrite-objc main.m指令,可以将OC源码转换成C语言源代码,但要注意这里转换出来的C源码并不是最终程序执行的源码,只是过程代码,所以仅便于我们从更加底层的了解OC源码。源码无错且执行完毕后同级目录下便会出现main.cpp文件。

1.若要指定模拟器环境下运行:
首先可执行xcodebuild -showsdks查看本地装有的SDK
然后执行xcrun -sdk iphonesimulatorx.x clang -rewrite-objc main.m(x.x即为本机安装的模拟器版本)
2.若指定真机运行
xcrun -sdk iphoneosx.x clang -rewrite-objc main.m
3.若代码中import了第三方的SDK,可以通过下列命令关闭
xcrun -sdk iphonesimulator10.0 clang -rewrite-objc -F /Users/Desktop main.m
注意,在mac系统下,无需先建工程再执行源码转化,在任意目录先新建一个m文件即可转换。

2.Block源码

先举一个简单的block,方便查看Block的结构。

OC源码:
#import <Foundation/Foundation.h>
int main(int argc, char * argv[]) {
      @autoreleasepool {  
          void (^aBlock)() = ^{
                                  NSLog(@"Hello world !");
                               };
          aBlock();
      }  
    return 0;
}

转换后的相关C源码如下:(转换出来的源码虽然有九万多行,但大部分是因为我导入了Foundation框架,以下只列出与main有关的部分源码)

Block c源码截图.png

虽然书中已经写的很详细了,但我这里重新理一遍理解的思路,一方面是便于自己梳理要点便于记忆,另一方面有关于书中有些疑问,在这里记下来,方便日后交流与回顾。
下面根据上图中的标号顺序来理解这段转换成C源码的实现原理:
1.先来看看一眼就能认出的block内容部分,在OC中添加的block内容为^{NSLog(@"Hello world !");};在标注1的地方正好能看到熟悉的NSLog,所以可以看出静态函数__main_block_func_0即对应OC中block中执行的函数部分。

2.接着来看main_block_func_0中传递的参数为 main_block_impl_0结构体类型的 cself指针,这里cself就相当于self(不做拓展介绍,更多的细节请自己查阅更加底层的资料)。然后来具体看__main_block_impl_0结构体,在标注2的地方即为这个结构体的成员变量与构造函数。
这个结构体包含两种变量与一个构造函数:

前两种结构体变量稍后讲述,先看一下构造函数main_block_impl_0中所传递的参数为void *fp,struct __main_block_desc_0 *desc, int flags=0
其中fp即为指向block要实现的函数指针,desc为block的相关描述信息,直接赋给Desc变量,最后是带默认值的flags变量。

3.先来看__main_block_impl_0结构体:

4.再来看__main_block_desc_0:block 的相关描述信息结构体

5.main函数部分
void (*aBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
这句能猜到就对应着void (^aBlock)() = ^{NSLog(@"Hello world !");};
将其强制转化等操作去掉后,就仅剩下了*aBlock = &__main_block_impl_0;就可以看出这就是一个简单的指针赋值,将main_block_impl_0结构体赋值给aBlock指针。我的理解为通过构造函数构造出一个main_block_impl_0结构体并赋给了一个指针,这个即为block的c层面上的理解。
6.最后看main中的调用block部分:
OC: aBlock(); C: ((void (*)(__block_impl *))((__block_impl *)aBlock)->FuncPtr)((__block_impl *)aBlock);
这句代码可简化为*aBlock->impl.FuncPtr,即通过aBlock变量中的impl的FuncPtr函数指针调用函数。

上一篇下一篇

猜你喜欢

热点阅读