Block底层实现系列 一 基础篇

通过这篇博文, 你可以了解到不访问/访问外部变量, 不修改外部变量的Block被转换到C++后的实现方法.

不访问外部变量, 不修改外部变量

void callBlock(void (^myblock)(void)){
    myblock();
}
void testBlock(){

    void (^testBlock)(void) = ^{
        printf("执行block");
    };
    callBlock(testBlock);
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        testBlock();
    }
    return 0;
}

使用命令行

$ clang -rewrite-objc main.m

被转换到如下代码(只挑选出Block的转换部分)

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
void callBlock(void (*myblock)(void)){
    ((void (*)(__block_impl *))((__block_impl *)myblock)->FuncPtr)((__block_impl *)myblock);
}
struct __testBlock_block_impl_0 {
    struct __block_impl impl;
    struct __testBlock_block_desc_0* Desc;
  __testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself) {
        printf("执行block");
}
static struct __testBlock_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __testBlock_block_desc_0_DATA = { 0, sizeof(struct __testBlock_block_impl_0)};
void testBlock(){
    void (*testBlock)(void) = ((void (*)())&__testBlock_block_impl_0((void *)__testBlock_block_func_0, &__testBlock_block_desc_0_DATA));
    callBlock(testBlock);
}
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        testBlock();
    }
    return 0;
}

代码行数增加了很多, 我们一点点的说.
先看最熟悉的

static void __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself) {
        printf("执行block");
}

我们写的block内部执行语句, 最后会转换到这个方法内部. 在看看这个方法是被谁调用的.搜索一下会发现, __testBlock_block_func_0只有在testBlock() 方法中出现过, 我们来看看这个方法

void testBlock(){
    void (*testBlock)(void) = ((void (*)())&__testBlock_block_impl_0((void *)__testBlock_block_func_0, &__testBlock_block_desc_0_DATA));
    callBlock(testBlock);
}

这段代码各种转换的有点乱了, 如果简化一下

void testBlock(){
   __testBlock_block_impl_0 *testBlock = &__testBlock_block_impl_0(__testBlock_block_func_0,&__testBlock_block_desc_0_DATA);
    callBlock(testBlock);
}

__testBlock_block_func_0 被传到__testBlock_block_impl_0的内部了. 来看看__testBlock_block_impl_0

struct __testBlock_block_impl_0 {
    struct __block_impl impl;
    struct __testBlock_block_desc_0* Desc;
  __testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

impl.FuncPtr = fp; 说明我们的__testBlock_block_func_0被传入到impl->FuncPtr的变量中.

回到void testBlock() 中, 再看第二行 callBlock(testBlock);

void testBlock(){
   __testBlock_block_impl_0 *testBlock = &__testBlock_block_impl_0(__testBlock_block_func_0,&__testBlock_block_desc_0_DATA);
    callBlock(testBlock);
}

实例化了一个__testBlock_block_impl_0类型变量, 并传递到了callBlock方法中.

void callBlock(void (*myblock)(void)){
    ((void (*)(__block_impl *))((__block_impl *)myblock)->FuncPtr)((__block_impl *)myblock);
}

这段代码简化一下后变成如下

void callBlock(void (*myblock)(void)){
   // ((void (*)(__block_impl *))((__block_impl *)myblock)->FuncPtr)((__block_impl *)myblock);
   *myblock->impl_>FuncPtr(myblock);
}

FuncPtr(myblock) 上文说到, 我们的__testBlock_block_func_0被传入到impl->FuncPtr的变量中. 这一行就调用__testBlock_block_func_0方法, 同时将__testBlock_block_impl_0的实例变量myblock传入方法中.
到此就完成的方法的调用.

访问外部变量, 不修改外部变量

void callBlock(void (^myblock)(void)){
    myblock();
}
void testBlock(){
    int aaa = 333;
    void (^testBlock)(void) = ^{
        printf("block 访问外部变量 %d", aaa);
    };
    callBlock(testBlock);
}
int main(int argc, const char * argv[]) {
        testBlock();
    return 0;
}

转换之后的代码:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
void callBlock(void (*myblock)(void)){
    ((void (*)(__block_impl *))((__block_impl *)myblock)->FuncPtr)((__block_impl *)myblock);
}
struct __testBlock_block_impl_0 {
  struct __block_impl impl;
  struct __testBlock_block_desc_0* Desc;
  int aaa;
  __testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int _aaa, int flags=0) : aaa(_aaa) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself) {
  int aaa = __cself->aaa; // bound by copy
        printf("block 访问外部变量 %d", aaa);
 }
static struct __testBlock_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __testBlock_block_desc_0_DATA = { 0, sizeof(struct __testBlock_block_impl_0)};
void testBlock(){
    int aaa = 333;
    void (*testBlock)(void) = ((void (*)())&__testBlock_block_impl_0((void *)__testBlock_block_func_0, &__testBlock_block_desc_0_DATA, aaa));
    callBlock(testBlock);
}
int main(int argc, const char * argv[]) {
        testBlock();
    return 0;
}

先看下这段代码

void testBlock(){
    int aaa = 333;
    void (*testBlock)(void) = ((void (*)())&__testBlock_block_impl_0((void *)__testBlock_block_func_0, &__testBlock_block_desc_0_DATA, aaa));
    callBlock(testBlock);
}

我们访问的外部变量aaa = 333 被传入到__testBlock_block_impl_0中. 再看__testBlock_block_impl_0

struct __testBlock_block_impl_0 {
  struct __block_impl impl;
  struct __testBlock_block_desc_0* Desc;
  int aaa;
  __testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int _aaa, int flags=0) : aaa(_aaa) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

对比不访问外部变量的block现在的 __testBlock_block_impl_0 结构体刚好多了一个成员变量aaa. 说明我们访问的外部变量在转换后, 会变成__testBlock_block_impl_0实例的成员变量.

调用的过程与不访问外部变量的步骤基本一直. 这里不在赘述,

static void __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself) {
  int aaa = __cself->aaa; // bound by copy
        printf("block 访问外部变量 %d", aaa);
 }

这个方法与不访问外部变量方法不太一样, 多了一行int aaa = __cself->aaa;
上文我们提到aaa被传入到__testBlock_block_impl_0结构体实例testBlock中, 通过callBlock方法传递给了__testBlock_block_func_0, 这里的 __cself指的就是testBlock实例.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值