iOS探究Block的实现过程

对于学习过Objective-C的人来说,一定知道OC中神奇的代码块block,下面我们就来讲一讲block

Block实际上是作为极普通的C语言源码来处理的:含有Block语法的源码首先被转换成C语言编译器能处理的源码,再作为普通的C源代码进行编译。

使用LLVM编译器的clang命令可将含有Block的Objective-C代码转换成C++的源代码,以探查其具体实现方式:

clang -rewrite-objc 源码文件名

我们在Xcode中创建一个Command Line Tool项目,语言选择Objective-C,代码如下:

#import 

int (^blk)(int) = ^(int count){
    return count+1;
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ^{};
        blk(1);
    }
    return 0;
}

经过clang转化后我们会得到一个main.cpp的c++文件打开后发现,这个文件代码有9万多行,前面都是Foundation框架的代码,我们不用去在意,将文件移到最后就能看到我们所需要的代码了。

struct __blk_block_impl_0 {
  struct __block_impl impl;
  struct __blk_block_desc_0* Desc;
  __blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteGlobalBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static int __blk_block_func_0(struct __blk_block_impl_0 *__cself, int count) {

    return count+1;
}

static struct __blk_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __blk_block_desc_0_DATA = { 0, sizeof(struct __blk_block_impl_0)};
static __blk_block_impl_0 __global_blk_block_impl_0((void *)__blk_block_func_0, &__blk_block_desc_0_DATA);
int (*blk)(int) = ((int (*)(int))&__global_blk_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;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
}

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)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        ((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 1);
    }
    return 0;
}

我们还需要 Foundation框架中block结构体的定义

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

isa:指向block的类的指针:block一共有三个类

  • _NSConcreteStackBlock:在栈上创建的Block对象
  • _NSConcreteMallocBlock:在堆上创建的Block对象
  • _NSConcreteGlobalBlock:全局数据区的Block对象

FuncPtr:指向block实现方法的指针

我们先来看main函数中的block

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;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
}

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是block的完整结构体,
__block_impl是一个block的结构体
__main_block_desc_0是一个block描述的结构体其中
reserved是保留的字节数
Block_size是block结构体的空间,大小就是sizeof(struct __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;
  }

main函数中我们本来写的^{}转化为了((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));一串复杂的代码,其实就是构造了一个__main_block_impl_0结构体转化成代码就是


  __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA,int flag=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = __main_block_func_0;
    Desc = __main_block_desc_0_DATA;
  }

这样就定义了一个完整的__main_block_impl_0结构体。

下面我们来看下blkBlock的实现。

struct __blk_block_impl_0 {
  struct __block_impl impl;
  struct __blk_block_desc_0* Desc;
  __blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteGlobalBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static int __blk_block_func_0(struct __blk_block_impl_0 *__cself, int count) {
    return count+1;
}

static struct __blk_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __blk_block_desc_0_DATA = { 0, sizeof(struct __blk_block_impl_0)};
static __blk_block_impl_0 __global_blk_block_impl_0((void *)__blk_block_func_0, &__blk_block_desc_0_DATA);
int (*blk)(int) = ((int (*)(int))&__global_blk_block_impl_0);

大致和上一个block相同,不过blk是一个全局的block,定义的时候也在main方法外面,isa指针的值为&_NSConcreteGlobalBlock

调用blk时代码为

((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 1);

从中可以看见调用的就是__block_impl结构体的FuncPtr 是一个void指针,指向的就是static int __blk_block_func_0(struct __blk_block_impl_0 *__cself, int count)这个静态方法。

到这里,我们就能理解block是如何实现的了。

总结一下,block在c语言中是以结构体的方法存储的,在Objective-C中是当做对象来使用的,block有三种不同的类型,分别是全局block,栈block和堆block。

你可能感兴趣的:(iOS探究Block的实现过程)