老青菜

iOS block 形参变量

2014-09-30

上一篇 中我们了解了block局部变量的使用。

block形参就是定义block带的参数,和函数的参数使用一样,我们可以在block随意使用修改block形参。

我们来看个例子:

我们声明了两个NSString 指针_p1 _p2、int 型_p3、可变数组_p4,并把这些参数传入block,在block内修改。

    -(void )test3
    {
        NSString *_p1=[NSString stringWithFormat:@"hello %@",@"world11"];
        NSString *_p2=[NSString stringWithFormat:@"hello %@",@"world12"];
        int _p3=1;
        NSMutableArray *_p4=[[NSMutableArray alloc]init];
        //初始值
        NSLog(@"init p1:%@,%p,%p",_p1,_p1,&_p1);
        NSLog(@"init p2:%@,%p,%p",_p2,_p2,&_p2);
        NSLog(@"init p3:%d,%p",_p3,&_p3);
        NSLog(@"init p4:%@,%p,%p",_p4,_p4,&_p4);
        void (^myBlock)(NSString *,NSString **,int,NSMutableArray *) = ^(NSString *p1,NSString **p2,int p3,NSMutableArray * p4) {
            //block内赋值
            p1=@"21";
            *p2=@"22";
            p3=23;
            [p4 addObject:@"23"];
            NSLog(@"excuteing p1:%@,%p,%p",p1,p1,&p1);
            NSLog(@"excuteing p2:%@,%p,%p,%p",*p2,*p2,p2,&p2);
            NSLog(@"excuteing p3:%d,%p",p3,&p3);
            NSLog(@"excuteing p4:%@,%p,%p",p4,p4,&p4);
        };
        myBlock(_p1,&_p2,_p3,_p4);
        //block执行后
        NSLog(@"excuteafter p1:%@,%p,%p",_p1,_p1,&_p1);
        NSLog(@"excuteafter p2:%@,%p,%p",_p2,_p2,&_p2);
        NSLog(@"excuteafter p3:%d,%p",_p3,&_p3);
        NSLog(@"excuteafter p4:%@,%p,%p",_p4,_p4,&_p4);
    }

输出日志:

    2014-07-31 14:17:06.774 Test[3655:60b] init p1:hello world11,0x16537b20,0x27df8990
    2014-07-31 14:17:06.776 Test[3655:60b] init p2:hello world12,0x1652d760,0x27df898c    
    2014-07-31 14:17:06.778 Test[3655:60b] init p3:1,0x27df8988    2014-07-31 14:17:06.779 Test[3655:60b] init p4:(),0x1652b6d0,0x27df8984
    2014-07-31 14:17:06.781 Test[3655:60b] excuteing p1:21,0x11a08,0x27df893c    
    2014-07-31 14:17:06.782 Test[3655:60b] excuteing p2:22,0x11a18,0x27df897c,0x27df8938    
    2014-07-31 14:17:06.783 Test[3655:60b] excuteing p3:23,0x27df8934    2014-07-31 14:17:06.784 Test[3655:60b] excuteing p4:(23),0x1652b6d0,0x27df8930    
    2014-07-31 14:17:06.785 Test[3655:60b] excuteafter p1:hello world11,0x16537b20,0x27df8990    
    2014-07-31 14:17:06.786 Test[3655:60b] excuteafter p2:22,0x11a18,0x27df898c    
    2014-07-31 14:17:06.787 Test[3655:60b] excuteafter p3:1,0x27df8988
    2014-07-31 14:17:06.788 Test[3655:60b] excuteafter p4:(
    23),0x1652b6d0,0x27df898

从日志可以看出:

    1. 我们把指针_p1(NSString *)传入block,传参数的过程其实可以理解为用新的指针指向 _p1所指向的内存地址。这时候在block内部修改指针指向,只是修改了block形参的指针的指向,所以block内部_p1的内容和block外部_p1的不一样。

    2. 我们把指针_p4NSMutableArray *)传入block,传参数的过程和上面一样,用了一个新的指针指向 参数所指向的内存地址,在block内部对 形参指针指向的内存进行修改,即操作的是同一片内存,所以能同步到block外部。

    3. 我们把指针_p2(NSString *)传到了block内部,传递的是指针的地址,我们在block内部修改`p2,其实是修改 指针p2` 指向的指针 指向的内存地址,也能同步到block外部。

    4. 因为_p3是值传递,所以block内部修改不会影响外部,这个和函数一样。

我们来看看block对行参变量的处理:

这是block结构体,并没有生成成员 对形参进行引用

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

我们看block实现的函数,对变量的访问,都是通过形参来实现的,并没有通过结构体的成员

    static void __KDBlockTest__test3_block_func_0(struct __KDBlockTest__test3_block_impl_0 *__cself, NSString *p1, NSString **p2, int p3, NSMutableArray *p4) {
            p1=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_9;
            *p2=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_10;
            p3=23;
        ((void (*)(id, SEL, id))(void *)objc_msgSend)((id)p4, sel_registerName("addObject:"), (id)(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_11);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_12,p1,p1,&p1);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_13,*p2,*p2,p2,&p2);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_14,p3,&p3);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_15,p4,p4,&p4);
    }

下面是我们的测试函数,看红色部分,是通过函数参数传递,和函数调用一样,和block内使用局部变量、静态变量、成员变量还是有很大区别的,并没有截获变量。

    static void _I_KDBlockTest_test3(KDBlockTest * self, SEL _cmd) {
            NSString *_p1=((NSString *(*)(id, SEL, NSString *, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_1, (NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_2);
            NSString *_p2=((NSString *(*)(id, SEL, NSString *, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_3, (NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_4);
            int _p3=1;
            NSMutableArray *_p4=((id (*)(id, SEL))(void *)objc_msgSend)((id)((id (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("alloc")), sel_registerName("init"));

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_5,_p1,_p1,&_p1);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_6,_p2,_p2,&_p2);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_7,_p3,&_p3);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_8,_p4,_p4,&_p4);
            void (*myBlock)(NSString *,NSString **,int,NSMutableArray *) = (void (*)(NSString *, NSString **, int, NSMutableArray *))&__KDBlockTest__test3_block_impl_0((void *)__KDBlockTest__test3_block_func_0, &__KDBlockTest__test3_block_desc_0_DATA);
            ((void (*)(__block_impl *, NSString *, NSString **, int, NSMutableArray *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock, _p1, &_p2, _p3, _p4);//block 执行
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_16,_p1,_p1,&_p1);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_17,_p2,_p2,&_p2);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_18,_p3,&_p3);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_654672_mi_19,_p4,_p4,&_p4);
    }

总结:

    1. 对于block形参,block不会对形参、实参强引用,调用是通过参数传递实现的(block结构体内部没有成员引用)

    2. 和函数调用一样,block内部可以修改形参,并且影响到实参。

Tags: objc
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章