前言
最近在总结关于block的东西,可能会写几篇关于block的博客,语法那些就不说,这里提供一个语法的入口,我们主要探究block的底层实现、block引用局部变量、block的内存管理,今天先讲下block的底层实现。
block实例
| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 | #import <Foundation/Foundation.h>
void hello_(){
    int i = 2;
    int j = 3;
    long (^myBlock)(void) = ^long(){
        return i*j*10;
    };
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
    }
    return 0;
}
 | 
定义hello方法是方便我们等下查看编译代码的时候准确定位myBlock的实现。用clang将objected-C 代码编译为C++代码。
| 1
 | 控制台命令:clang -rewrite-objc main.m
 | 
我们打开main.cpp文件,定位到以下代码:
| 1
2
3
4
5
6
 | struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
 | 
这是block基类的定义,所有block都继承于它,拥有它以下那些属性:  
isa是指向该block的类型的类,也就是我们理解的对象指针;  
Flags包含了引用计数;  
Reserved:保留变量,我的理解是表示block内部的变量数;      
FuncPtr:函数指针,指向具体block实现的函数的调用地址;
| 1
2
3
4
5
6
7
8
9
10
11
12
 | struct __hello__block_impl_0 {
  struct __block_impl impl;
  struct __hello__block_desc_0* Desc;
  int i;
  int j;
  __hello__block_impl_0(void *fp, struct __hello__block_desc_0 *desc, int _i, int _j, int flags=0) : i(_i), j(_j) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
 | 
这是myBlock的定义,它的结构体以hello打头,代表myBlock位于hello函数里,这是block的命名方式。helloblock_desc_0指的是myBlock的描述。
| 1
2
3
4
5
6
 | static long __hello__block_func_0(struct __hello__block_impl_0 *__cself) {
  int i = __cself->i; // bound by copy
  int j = __cself->j; // bound by copy
        return i*j*10;
    }
 | 
 myBlock内函数的具体实现,注意bound by copy,block对外部引用变量做只读拷贝,还有另外的一种情况,在以后讲到关于block引用局部变量的时候会说。
| 1
2
3
4
 | static struct __hello__block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __hello__block_desc_0_DATA = { 0, sizeof(struct __hello__block_impl_0)};
 | 
这是myBlock的描述
| 1
2
3
4
5
 | void hello_(){
    int i = 2;
    int j = 3;
    long (*myBlock)(void) = ((long (*)())&__hello__block_impl_0((void *)__hello__block_func_0, &__hello__block_desc_0_DATA, i, j));
}
 | 
myBlock实例变量的定义
做下小小总结
block是对象;  
编译器会根据block捕获的局部变量,生成具体的结构体定义。block内部的代码将会提取出来,成为一个单独的C函数(eg:helloblock_func_0)。创建block时,实际就是在方法中声明一个struct,并且初始化该struct的成员。而执行block时,就是调用那个单独的C函数,并把该struct指针传递过去;          
block中包含了被引用的局部变量(由struct持有),也包含了控制成分的代码块(由函数指针持有),符合闭包(closure)的概念。