iOSer 的自我修养Objective-CiOS开发

关于OC-高级编程中Block的总结

2016-07-11  本文已影响1147人  上帝也是码农

一、block基础

1、什么是block?

作者的原话:带有自动变量的匿名函数。
在这里解释一下两个词,第一个词:自动变量,在执行block的时候如果它的代码块包含局部变量它会自动截取该变量,下文会举例子说明。第二个词:匿名函数,该词说明了block的具体的作用相当于一个函数(OC中的方法),但是你不用为它命名。
首先,block是OC中的一个对象,所以结合上面的理解block的本质就是一个存储代码段的OC对象。

2、block的语法

2.1、语法结构
返回值类型 (^blockName)(参数类型) = ^(参数列表){
body;
};
eg:

int (^firstBlock)(int,int)= ^(int x,int y){
  return x + y;
};

2.2、block的三种情况
1)无返回值无参数

    - (void)firstFunc{
    void (^firstBlock)() = ^{
        NSLog(@"无参数无返回值");
    };
    firstBlock();
    }

2)无返回值有参数

  - (void)secondFunc{
    void (^secondBlock)(int) = ^(int x){
        NSLog(@"有参数无返回值.%d",x);
    };
    secondBlock(666);
    }

3)有返回值有参数

    - (void)thirdFunc{
    int (^thirdBlock)(int, int) = ^(int x,int y){
        NSLog(@"有参数,有返回值");
        return x + y;
    };
    int total = thirdBlock(2,3);
    NSLog(@"total = %d",total);
    }

3、block最常用的方式:控制器反向传值(eg:注册界面将信息返回给登录界面)

具体步骤:
1)在A控制器实现B控制器定义的block(如果是值从B传到A)

  __weak ViewController *weakSelf = self;
    secondVC.deliverStrBlock = ^ (NSString *deliverStr) {
        weakSelf.testStr = deliverStr;
        NSLog(@"%@",weakSelf.testStr);
    };

2)在B的.h文件声明block

typedef void (^DeliverStrBlock) (NSString *str);
@property (nonatomic, copy) DeliverStrBlock deliverStrBlock;

3)在B的.m文件调用block
实例代码:

 if (self.deliverStrBlock) {
        self.deliverStrBlock(@"test str");
    }

二、block进阶

1、如何在block中改变局部变量的值?

  NSString *a = @"5";
    int (^thirdBlock)(int, int) = ^(int x,int y){
        a = @"haha";
        NSLog(@"有参数,有返回值 %@",a);
        return x + y;
    };

如果你在代码中写了上述代码,编译器会报错,错误提示为:你不能分配该变量的值缺少__block 如果我们想搞清楚为什么会报这个错误就要从block的源码上分析了,在高级编程这本书中,作者提到在block截取变量的时候回自动生成一个同名的变量,并将截取的值赋给该变量,所以此a非彼a,block中的a并不会指向代码段外的a的内存地址,所以你改变此值并不能改变block外的变量a的值,因此,编译器才会报错。
但是当你在NSString 前面加上__block 的前缀时,该代码则会正常运行。
代码示例:

 __block NSString *a = @"5";
    int (^thirdBlock)(int, int) = ^(int x,int y){
        a = @"haha";
        return x + y;
    };

那么问题来了,为什么在a的前面加上前缀修饰符__block该代码就能正常运行呢?要想解决该问题还要去分析block的源码。在加上__block的修饰符,在block的生成结构体实例的时候,__block也会生成一个结构体实例,并且在该结构体中有一个__forwording的指针指向截取的变量的内存地址,而block的__main_block_impl_0结构体实例持有指向__block变量__Block_byref_val_0结构体实例的指针,所以block可以拥有指向截取变量内存地址的指针。
所以当添加了__block之后再block中就可以修改变量的值。

2、如何避免循环引用

这个问题应该是面试或者实际情况中遇到最多的一个问题了。
解决循环引用的两个方法
1)使用了__weak,如果在上述代码中不将控制器弱引用block的话就会发生循环引用而无法释放的问题。(A控制器拥有block,而且block拥有A)。

  __weak ViewController *weakSelf = self;

2)使用__block(self拥有block,block拥有__block__block拥有self)
注意:此方式必须执行一次block。即执行tmp = nil,来打断循环链。

    __block id tmp = self;
    secondVC.deliverStrBlock = ^ (NSString *deliverStr) {
        NSLog(@"%@",tmp);
        tmp = nil;
    };

3、使用typedef定义block

如果你也觉得block的语法结构很反人类,你可以使用typedef来重命名block

typedef void (^DeliverStrBlock) (NSString *str);
@property (nonatomic, copy) DeliverStrBlock deliverStrBlock;

4、结束语

在一开始读《Objective-C高级编程》这本书的时候,也是一脑的懵逼,但是当静下心来的时候多读几遍这本书就会对block有很深的认识。推荐大家多读几遍,本文中有写的不对或理解有偏差的地方欢迎大家指正

上一篇 下一篇

猜你喜欢

热点阅读