iOS底层系列之<9>--Block(二)变量捕获Capture

1、变量捕获(1)

  • 下面代码打印结果是?
- (void)testBlock {
    
    int age = 10;
    
    void (^block1)(void) = ^(){
        NSLog(@"--testBlock--%d",age);
    };
    age = 20;
    block1();
}

  • 我们先不着急公布答案,我们可以来看看c++代码是怎么样的。
  • 转换成c++代码方式请看 这里
static void _I_ViewController_testBlock(ViewController * self, SEL _cmd) {

    int age = 10;

	// 这里的第三个参数,age:是值传递,相当于把10传递进去了
    void (*block1)(void) = ((void (*)())&__ViewController__testBlock_block_impl_0((void *)__ViewController__testBlock_block_func_0, &__ViewController__testBlock_block_desc_0_DATA, age));
    age = 20;
    ((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
}
// 再看下面这段代码,:age(_age)是c++语法,意思是把参数直接赋值给了int age;
// 所以,这个时候,block内部捕获的age值是10了
struct __ViewController__testBlock_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController__testBlock_block_desc_0* Desc;
  int age;
  __ViewController__testBlock_block_impl_0(void *fp, struct __ViewController__testBlock_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
// 再看最后实现方法
// int age的值就是从__ViewController__testBlock_block_impl_0取出来的
// 打印结果就会是10了
// 而且  int age = __cself->age; // bound by copy 
// 写了是copy的意思,就是复制过来的了
static void __ViewController__testBlock_block_func_0(struct __ViewController__testBlock_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_rxjvg1rx6hs778r_253ckdt40000gn_T_ViewController_fbfb1a_mi_0,age);
    }

打印结果:–testBlock–10

2、变量捕获(2)

  • 那么我们接下来再看下另外一种类似的情况,带有__block修饰
- (void)testBlock {
    
    __block int age = 10;
    
    void (^block1)(void) = ^(){
        NSLog(@"--testBlock--%d",age);
    };
    age = 20;
    block1();
}
  • 同样的,我们还是先来一起看看C++代码的实现
// 可以看到,我们的age这次不是值传递哦,而是带&,这是地址传递了
static void _I_ViewController_testBlock(ViewController * self, SEL _cmd) {

    __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};

    void (*block1)(void) = ((void (*)())&__ViewController__testBlock_block_impl_0((void *)__ViewController__testBlock_block_func_0, &__ViewController__testBlock_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
    (age.__forwarding->age) = 20;
    ((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
}
// 注意到,这次的age是引用的方式了,ref
// age(_age->__forwarding)虽然对C++语法不是很熟,但是我们猜测得到,这是指向age的地址的意思
struct __ViewController__testBlock_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController__testBlock_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref
  __ViewController__testBlock_block_impl_0(void *fp, struct __ViewController__testBlock_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
// 再看这里
// __Block_byref_age_0 *age = __cself->age; // bound by ref
// ref 就是引用的意思,还是引用age的地址,而不是直接复制值
static void __ViewController__testBlock_block_func_0(struct __ViewController__testBlock_block_impl_0 *__cself) {
  __Block_byref_age_0 *age = __cself->age; // bound by ref

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_rxjvg1rx6hs778r_253ckdt40000gn_T_ViewController_1e09b7_mi_0,(age->__forwarding->age));
    }

所以,我们可以大胆猜测得到,block内部的值被修改了
实际上打印的结果:–testBlock–20
证实了我们的猜测!

3、变量捕获(3)

- (void)testBlock {
    // auto :局部变量的默认修饰关键字
    auto int weight = 1;
    __block int age = 2;
    static int height = 3;
    
    void (^block1)(void) = ^(){
        NSLog(@"--testBlock--%d--%d--%d",weight,age,height);
    };
    weight = 4;
    age = 5;
    height = 6;
    block1();
}
  • C++代码
static void _I_ViewController_testBlock(ViewController * self, SEL _cmd) {

    auto int weight = 1;
    __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 2};
    static int height = 3;

	// 这里可以看到,带&的就是指针传递
    void (*block1)(void) = ((void (*)())&__ViewController__testBlock_block_impl_0((void *)__ViewController__testBlock_block_func_0, &__ViewController__testBlock_block_desc_0_DATA, weight, &height, (__Block_byref_age_0 *)&age, 570425344));
    weight = 4;
    (age.__forwarding->age) = 5;
    height = 6;
    ((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
}
  • 同理可以猜出,带有&的,会被修改值

打印结果: --testBlock–1--5–6
也是符合我们的猜想的

4、变量捕获(4)

  • 代码再改下
int ageGlobal = 10;
static int heightGlobal = 10;

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self testBlock];
}

- (void)testBlock {
    void (^block1)(void) = ^(){
        NSLog(@"--testBlock--%d--%d",ageGlobal,heightGlobal);
    };
    
    ageGlobal = 20;
    heightGlobal = 20;
    
    block1();
}
  • 转C++代码
// 这次怎么没有传递ageGlobal 和 heightGlobal的值或者是指针呢?
static void _I_ViewController_testBlock(ViewController * self, SEL _cmd) {


    void (*block1)(void) = ((void (*)())&__ViewController__testBlock_block_impl_0((void *)__ViewController__testBlock_block_func_0, &__ViewController__testBlock_block_desc_0_DATA));

    ageGlobal = 20;
    heightGlobal = 20;

    ((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
}
  • 再看这部分代码,发现也是全局变量
  • C++代码也是直接访问全局变量,所以没必要引用或传递值了
int ageGlobal = 10;
static int heightGlobal = 10;


static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));

    ((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("testBlock"));
}


struct __ViewController__testBlock_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController__testBlock_block_desc_0* Desc;
  __ViewController__testBlock_block_impl_0(void *fp, struct __ViewController__testBlock_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __ViewController__testBlock_block_func_0(struct __ViewController__testBlock_block_impl_0 *__cself) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_qw_rxjvg1rx6hs778r_253ckdt40000gn_T_ViewController_1c9270_mi_0,ageGlobal,heightGlobal);
    }

最后打印结果:–testBlock–20–20

5、再看一个

- (void)testBlock {
   
    void (^block1)(void) = ^(){
        NSLog(@"%@",self);
    };
    
    block1();
}

这个会不会self,会不会被捕获?

答案是会,因为self是参数

在OC里面,每一个函数调用有两个默认的参数
- (void)testBlock(self, (SEL)sel){
}
既然self是参数,参数就是局部变量了,局部变量就会被捕获。

6、总结

1、局部变量会被捕获,全局变量不会被捕获
2、static 修饰的或者是__block修饰的了,是指针传递
3、auto 修饰的,是值传递

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值