Objective-C中Blocks块的介绍

*注:解释内容主要参考《Objective-C 高级编程》

1.介绍:

  Blocks是C语言的扩充功能:带有自动变量(局部变量)的匿名函数。

  Blocks的语法:^  返回值类型  参数列表  表达式

  例如:

^int (int c){return c+1;}
其中, 返回值类型 是可以省略的,会按return类型返回,如果不需要参数,那么 参数列表 也可以省略,如:

^{printf("hello");}

2.Block类型变量

    int (^blk)(int) = ^(int c){return c+1;};
    
    int x = blk(5);
    
    NSLog(@"x = %d",x);
Block值 ^( int c){ return c+ 1 ;} 被赋值给了变量blk中,blk即为Block变量。

利用typedef定义Block别名:

    typedef int (^blk_1)(int);
    
    blk_1 blkk = ^(int x){return x+3;};
    
    NSLog(@"x = %d",blkk(3));
意指用blk_1来代替int ^(int);这种块,之后调用赋值什么的会更舒服。


3.截获自动变量值

    int x = 1;
    void (^blk)(void) = ^{
        NSLog(@"zhi = %d",x);
    };
    
    x = 6;
    
    blk();
以上代码输出结果 zhi = 1;可见在Block截获的是自动变量x的值,之后改变x,并不会对块的执行结果有影响。并且我们如果想在块中改变x的值是无法通过编译的。如果我们想在块中改变x的值,需要用__block说明符


4.__block说明符

    __block int x = 1;
    
    void (^blk)(void) = ^{
        x = 6;
    };

    blk();
    
    NSLog(@"x = %d",x);

输出结果为 x = 6

用__block修饰x后,便可以在块中改变截取的自动变量x,那么:

    __block int x = 1;
    
    void (^blk)(void) = ^{
        NSLog(@"x = %d",x);
    };

    x = 7;
    
    blk();
输出结果为 x = 7

这里思考,如果我们不用__block,截取OC对象,会怎么样:

    NSString *age = @"26";
    NSMutableDictionary *dic =[[NSMutableDictionary alloc]init];
    [dic setObject:age forKey:@"age"];
    
    void (^blk)(void) = ^{
        NSString *a = [dic objectForKey:@"age"];
        NSLog(@"age = %@",a);
    };
    
    NSString *age2 = [dic objectForKey:@"age"];
    age2 = @"18";
    [dic removeObjectForKey:@"age"];
    [dic setObject:age2 forKey:@"age"];
    
    blk();

输出结果为 age = 18

这里我们初始化了一个dic,里面有一个key为@"age"的NSString,初始值为26,在块中输出字典中字符串的值,然后在块执行前,改变dic中字符串。

由此我们联想:

    NSString *age = @"26";
    NSMutableDictionary *dic =[[NSMutableDictionary alloc]init];
    [dic setObject:age forKey:@"age"];
    
    void (^blk)(void) = ^{
        NSString *age2 = [dic objectForKey:@"age"];
        age2 = @"18";
        [dic removeObjectForKey:@"age"];
        [dic setObject:age2 forKey:@"age"];
    };
  
    blk();
    NSString *a = [dic objectForKey:@"age"];
    NSLog(@"age = %@",a);

输出 age = 18

所以在块中也能改变dic的值。

这里我们分析,我们用块截取的是NSMutableDIcitionary的实例指针,不能对其赋值,但是可以使用它,即可以向其中添加改变对象。

*这里我们要注意:

   const char text[] = "HelloWorld";
   const char *text1 = "HelloWorld";
    
    void (^blk)(void) = ^{
        printf("%c",text1[1]);
    };
    
    blk();
我们可以使用截取*text1,但是text[]不可以,因为截取自动变量的方法并没有实现对C语言数组的接获,应该用指针方法解决。

4.Blocks的实现

那么这种截取自动变量以及__block修饰变量,背后是怎样实现的呢,本文简单介绍一下有关截取变量这里,至于Blocks实现的详细解析放在后续文章中。

我们利用clang的指令转变一下我们的文件,打开终端,利用 clang -rewrite-objc ‘文件名’ 对我们的文件进行操作,我们的文件内容如下:

int main(){
    
    int i = 10;
    
    int (^blk)(void) = ^{
        int x = i+1;
        return x;
    };
    
    int jieguo = blk();
    
    return 0;
}
声明一个简单的块,截获了自动变量 int i,我们用clang指令操作它得到.cpp文件:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int i;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

//块中函数
static int __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int i = __cself->i; // bound by copy

        int x = i+1;
        return x;
}
由于转化后代码太多,就只截取了一部分。可以看到结构体构造函数初始化 i(_i) , 让i = _i,这是一个copy的过程,所以外部改变自动变量的值,不会影响到块中的值。

如果使用__block修饰的话:

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

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_i_0 *i; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static int __main_block_func_0(struct __main_block_impl_0 *__cself, int x) {
  __Block_byref_i_0 *i = __cself->i; // bound by ref

        int y = x + (i->__forwarding->i);
        return y;
}
可以看到,在构造函数中,i(_i->__forwarding) , 这里用一个结构体__Block_byref_i_0,以指针的方式去保存自动变量,保存了变量的地址,那么后续改变自动变量,块中的值也会相应改变。
有关块循环引用的问题,及各种使用会在后面文章中介绍。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值