首页 > 学院 > 开发设计 > 正文

Objective-C中Blocks块的介绍

2019-11-09 14:37:12
字体:
来源:转载
供稿:网友

*注:解释内容主要参考《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,以指针的方式去保存自动变量,保存了变量的地址,那么后续改变自动变量,块中的值也会相应改变。有关块循环引用的问题,及各种使用会在后面文章中介绍。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表