iOS 浅谈Block外部的变量传递

背景介绍

在iOS开发过程中,我们经常会用到block这个对象,它可以作为函数的回调、类的属性等出现在我们的项目中,作为回调,block相对于代理方法,代码量更少,使用起来更方便,还可以使用与block定义同一作用范围的变量。当然,block的使用会让代码的逻辑不如使用代理清晰,萝卜青菜,各有所爱。下面我就对于block可以使用与它在同一作用范围的变量这一特性展开说一下自己的理解。

正文

先看这样一段代码:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        __block int myNumber = 100;
        __weak NSMutableString * name = [NSMutableString stringWithString:@"apple"];
        int myANumber = 666;
        void (^block)(NSString *) = ^(NSString * smile){
            NSString * logStr = [smile stringByAppendingString:[NSString stringWithFormat:@"%d",myNumber]];
            NSLog(@"0---%@",logStr);
            NSLog(@"1---%@",[NSString stringWithFormat:@"%d",myNumber]);
            NSLog(@"1---%@",[NSString stringWithFormat:@"%d",myANumber]);
            NSLog(@"1---%@",name);
            [name appendString:@"big"];
            myNumber++;
        };
        block(@"hahaha~");
        NSLog(@"2----%@",[NSString stringWithFormat:@"%d",myNumber]);
        NSLog(@"2---%@",name);

    }
    return 0;
}

变量的名字定义的很丑陋。不要在意这些细节^_^。
看输出

2015-07-23 20:58:29.840 ceshi2[7020:2450289] 0---hahaha~100
2015-07-23 20:58:29.842 ceshi2[7020:2450289] 1---100
2015-07-23 20:58:29.842 ceshi2[7020:2450289] 1---666
2015-07-23 20:58:29.842 ceshi2[7020:2450289] 1---apple
2015-07-23 20:58:29.842 ceshi2[7020:2450289] 2----101
2015-07-23 20:58:29.842 ceshi2[7020:2450289] 2---applebig
Program ended with exit code: 0

在这段代码中block引用了外部的myNumber,myANumber,name三个变量。并对其中的myNumber name进行了修改。如果在block中加入myANumber++这样的代码,编译是不能通过的,也就是说block内部不能修改它的值。

干货来了:-)

使用__block修饰的基本类型变量,对象类型的变量我们可以在block中修改它的值,至于那个 __weak是为了解释另外一种场景,就是当我们在一个controller中引用了这个类本身的时候,经常会看到这样的代码__weak typedef(self) weakSelf = self ,这是为了防止block对self的强引用造成循环调用。
------------我是分割线-----------
对上述代码用clang进行转码,提取一些关键代码(最长的那部分是block的方法实现,可以略过)。

struct __block_impl {
  void *isa; //指向实例对象
  int Flags;//变量信息
  int Reserved;//保留
  void *FuncPtr;//指向方法实现
};
struct __Block_byref_myNumber_0 {
  void *__isa;
__Block_byref_myNumber_0 *__forwarding;
 int __flags;
 int __size;
 int myNumber;
};//把myNumber进行了封装

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int myANumber; //这个变量是值传递
  NSMutableString *__weak name; //这个变量传过来的是name的指针,所以不用__block修饰也可以修改,它的持有特性是__weak
  __Block_byref_myNumber_0 *myNumber; // by ref的变量
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _myANumber, NSMutableString *__weak _name, __Block_byref_myNumber_0 *_myNumber, int flags=0) : myANumber(_myANumber), name(_name), myNumber(_myNumber->__forwarding) { 
    impl.isa = &_NSConcreteStackBlock;//block在栈区 还可以在全局区 堆区(这三个用得多)
    impl.Flags = flags;
    impl.FuncPtr = fp; //指向方法实现__main_block_func_0
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, NSString *smile) {
  __Block_byref_myNumber_0 *myNumber = __cself->myNumber; // bound by ref 只有这个变量被封装成一个block对象
  int myANumber = __cself->myANumber; // bound by copy
  NSMutableString *__weak name = __cself->name; // bound by copy
//方法的实现部分(略过)
            NSString * logStr = ((NSString *(*)(id, SEL, NSString *))(void *)objc_msgSend)((id)smile, sel_registerName("stringByAppendingString:"), ((NSString *(*)(id, SEL, NSString *, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_1, (int)(myNumber->__forwarding->myNumber)));
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_2,logStr);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_3,((NSString *(*)(id, SEL, NSString *, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_4, (int)(myNumber->__forwarding->myNumber)));
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_5,((NSString *(*)(id, SEL, NSString *, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_6, (int)myANumber));
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_7,name);
            ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)name, sel_registerName("appendString:"), (NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_8);
            (myNumber->__forwarding->myNumber)++;
        }
 //这里有两个辅助函数
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->myNumber, (void*)src->myNumber, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->name, (void*)src->name, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->myNumber, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->name, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

可以看出,__block修饰的变量传递时进行了一次封装,变成__Block_byref_xxx_n这样的对象。指针传递给了block,所以可以修改myNumber的值,可能你对为什么封装后可以修改还是有疑问,那看一下main函数的实现。

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

        __attribute__((__blocks__(byref))) __Block_byref_myNumber_0 myNumber = {(void*)0,(__Block_byref_myNumber_0 *)&myNumber, 0, sizeof(__Block_byref_myNumber_0), 100}; //看这里,传进去了myNumber的地址,可以对照着看看__Block_byref_myNumber_0各个参数都对应指向什么。
        __attribute__((objc_gc(weak))) NSMutableString * name = ((NSMutableString *(*)(id, SEL, NSString *))(void *)objc_msgSend)((id)objc_getClass("NSMutableString"), sel_registerName("stringWithString:"), (NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_0);
        int myANumber = 666;
        void (*block)(NSString *) = (void (*)(NSString *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, myANumber, name, (__Block_byref_myNumber_0 *)&myNumber, 570425344);
        ((void (*)(__block_impl *, NSString *))((__block_impl *)block)->FuncPtr)((__block_impl *)block, (NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_9);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_10,((NSString *(*)(id, SEL, NSString *, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_11, (int)(myNumber.__forwarding->myNumber)));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_pv_m0472wsd6b9713094kjpvb0m0000gn_T_main_52e0f4_mi_12,name);

    }
    return 0;
}

block对象的定义我也在代码中给了注释。相信大家能看明白

后记

自己研究block花了一些时间,好似若有所得,又好像一无所获,在提高自我的路上,免不了一些坑和弯路,但我相信,功不唐捐,殊途同归,曾经我们以为是浪费时间的折腾最后会成为我们引以为傲的资本。引用孔子的一段话:“其为人也,发奋忘食,乐以忘忧,不知老之将至”,与君共勉。不足之处,还望不吝赐教。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值