objective c下的block模型

前言

通过clang将objective c代码翻译成cpp代码,阅读代码的实现去理解objc中block模型。

objective c代码

#import <Foundation/Foundation.h>
void foo_()
{
    int i = 2;
    NSNumber *num = @3;

    long (^myBlock)(void) = ^long() {
        return i * num.intValue;
    };

    long r = myBlock();
}

clang翻译objective c代码块

clang -rewrite-objc block.m

Objective c的block模型

首先我们简化一下cpp代码,大致如下:

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


static struct __foo__block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __foo__block_impl_0*, struct __foo__block_impl_0*);
  void (*dispose)(struct __foo__block_impl_0*);
} __foo__block_desc_0_DATA = { 0, sizeof(struct __foo__block_impl_0), __foo__block_copy_0, __foo__block_dispose_0};

struct __foo__block_impl_0 {
  struct __block_impl impl;
  struct __foo__block_desc_0* Desc;
  int i;
  NSNumber *num;
  __foo__block_impl_0(void *fp, struct __foo__block_desc_0 *desc, int _i, NSNumber *_num, int flags=0) : i(_i), num(_num) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static long __foo__block_func_0(struct __foo__block_impl_0 *__cself) {
  int i = __cself->i; // bound by copy
  NSNumber *num = __cself->num; // bound by copy

        return i * ((int (*)(id, SEL))(void *)objc_msgSend)((id)num, sel_registerName("intValue"));
}

void foo_(){
    int i = 2;
    NSNumber *num = ((NSNumber *(*)(id, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 3);

    long (*myBlock)(void) = ( (long (*)()) & __foo__block_impl_0((void *)__foo__block_func_0, &__foo__block_desc_0_DATA, i, num, 570425344));

    long r = ( (long (*)(__block_impl *)) ((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
  1. myBlock对象:在转换过后被转换为结构体struct __foo__block_impl_0,而struct __foo__block_impl_0结构体中含有isa指针,实际上它是一个对象,说明block其实是一个对象,有不同的存储形式,而在struct __foo__block_impl_0中有一句:impl.isa = &_NSConcreteStackBlock; 说明上述代码中myBlock对象实际上是存储在stack上的对象。

  2. foo_函数: 在转换过后形成静态函数static long __foo__block_func_0,函数的参数正是myBlock对象,在long __foo__block_func_0函数中直接操作myBlock对象的属性。

  3. 如果我们在myBlcok中修改i或者num的值,编译器会提示警告,通过struct __foo__block_impl_0的狗仔函数__foo__block_impl_0(void *fp, struct __foo__block_desc_0 *desc, int _i, NSNumber *_num, int flags=0) : i(_i), num(_num),可以看出实际上i和num值实际上赋值给了myBlock对象,所以在myBlock里面修改i或者num的值,修改的是block的成员变量,而不是通过参数传递进来的i,num,实际上就是c语法中的传递值和指针的区别。

这么说如果我们想修改i的值,参数传递指针就可以了嘛,说的没错,继续往下看你。

在block中如果我们想要修改 block外的值,只需要在值申明之前加上__block关键字即可。

下面接着分析含有__block变量的block模型

objective c代码

#import <Foundation/Foundation.h>
void foo_(){
    __block int i = 2;
    NSNumber *num = @3;

    long (^myBlock)(void) = ^long() {
        return i * num.intValue;
    };
    long r = myBlock();
}

clang翻译objective c代码块

clang -rewrite-objc block2.m

Objective c的block模型

struct __Block_byref_i_0 {
  void *__isa;
__Block_byref_i_0 *__forwarding;
 int __flags;
 int __size;
 int i;
};

struct __foo__block_impl_0 {
  struct __block_impl impl;
  struct __foo__block_desc_0* Desc;
  NSNumber *num;
  __Block_byref_i_0 *i; // by ref
  __foo__block_impl_0(void *fp, struct __foo__block_desc_0 *desc, NSNumber *_num, __Block_byref_i_0 *_i, int flags=0) : num(_num), i(_i->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

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

static long __foo__block_func_0(struct __foo__block_impl_0 *__cself) {
  __Block_byref_i_0 *i = __cself->i; // bound by ref
  NSNumber *num = __cself->num; // bound by copy

        return (i->__forwarding->i) * ((int (*)(id, SEL))(void *)objc_msgSend)((id)num, sel_registerName("intValue"));
    }

void foo_(){
    __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 2};
    NSNumber *num = ((NSNumber *(*)(id, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 3);

    long (*myBlock)(void) = ((long (*)())&__foo__block_impl_0((void *)__foo__block_func_0, &__foo__block_desc_0_DATA, num, (__Block_byref_i_0 *)&i, 570425344));

    long r = ((long (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
  1. myBlock对象:在转换过后被转换为结构体struct __foo__block_impl_0,不同于前面,多了一个__Block_byref_i_0的结构体,用来保存i的值
  2. 这里多了一个__Block_byref_i_0的结构体,而__Block_byref_i_0的结构体实际上也是一个对象,含有isa指针,在__foo__block_impl_0的构造函数中传递的是__Block_byref_i_0的指针,这样我们就可以直接在myBlock中对i的值进行修改,正如我们前面提到的指针传递。

希望通过本文,可以加深你对objective c的block模型的理解,如有错误,请提出,我会及时改正,大家共同学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值