美文网首页iOS学习开发
iOS探究Block的实现过程

iOS探究Block的实现过程

作者: 阳光下的小泡沫丶 | 来源:发表于2017-12-05 12:08 被阅读0次

    对于学习过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 <Foundation/Foundation.h>
    
    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的实现过程

        本文链接:https://www.haomeiwen.com/subject/vxxxixtx.html