iOS-Block02-小谈block的底层数据结构

在写这篇文章的时候,距离上一篇写block的文章已经过了很久了iOS- 谈谈自己知道的block(菜鸟的总结)。笔者也是想再次总结一些学习成果
这篇文章,主要是要分析下,block在系统编译之后,底层的展现到底是一个怎样的形式。首先我们还是通过clang的方式来编译我们的main.m文件,从而得到main.cpp文件。在cpp文件中,笔者只挑选出跟block有关的核心部分。首先,我们先来看看main.m的main函数部分:

  int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
       void (^block)(void) = ^{
            NSLog(@"这是一个block");
        };
        
        block();
        
    }
    return 0;
}

大家可以看到这是一个最简单的block定义和使用;然后我们再来看看cpp中main函数部分:

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

       void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

    }
    return 0;
}

乍一看可能大家看不懂,因为有很多(),这里其实涵盖了很多c++类型的强制转换,简化完之后的代码如下:

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        void (*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));
        block->FuncPtr(block);

    }
    return 0;
}

然后我们再看关键的几个地方:
1> __main_block_impl_0 这个其实我们不难找到,在cpp文件中已经定义了,是一个结构体:

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;
  }
};

所以,由此可见

 &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));

就是结构体的初始化,然后返回这个结构体的指针地址给block.
2> 再看这个结构体传入的两个参数 __main_block_func_0 / __main_block_desc_0_DATA 这两个在cpp中也是有所定义:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_tv_bzqp7v7d453fs34hx6hm6k9m0000gp_T_main_37c1f9_mi_0);
        }
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_func_0这个函数就是我们block中的nslog部分, __main_block_desc_0这个结构体包含了我们block对应的大小
3> 接下来解释下 block->FuncPtr(block) block对应的是__main_block_impl_0的地址,那block的地址其实就是__main_block_impl_0的第一个参数(struct __block_impl impl;)的地址,而第一个参数,我们可以继续在cpp中寻找:

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

而这个__block_impl结构体中就包含了FuncPtr,所以block->FuncPtr(block);可以这么写, 等价于 __main_block_func_0(block)
到此为止,大家应该对block的底层数据结构有所了解,当然有兴趣的同学可以试着去修改block,可以带参数编写,然后再去看看cpp文件中block不同的情况。有关于源码笔者就不写链接了,如果不懂clang的同学可以看下笔者之前的文章中有写,就是在main.m文件所在的文件夹中,执行一个中端的命令行:

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o mian-64.cpp

你可能感兴趣的:(iOS-Block02-小谈block的底层数据结构)