block 深入浅出

543 阅读2分钟
  • 首先block 是一个匿名函数

  • block 有捕获外部变量的能力

  • block 声明的时候是使用 copy 还是 strong

  • block的类型有哪些

  • 如何解决 block 的循环引用问题(只能使用 __weak 吗)

  • 为什么使用__block 可以在 block 内修改外部变量的值

  • 使用 clang 查看 block 内部实现

    1. block 是一个匿名函数,我们就要从其语法定义说起(show the code)

    • c函数(void返回值、funName 函数名、int a 参数、{printf(%d\n,a)函数表达式})
     void funcName(int a)  {
         printf("%d\n", a);
     };
    
    • block(^脱字符、int 返回值、void 参数、{return 3;};表达式)
    ^int (void){
            return 3;
        };
    
    • block使用我们可以看出右边的 block 块是有参数无返回值类型
    typedef void(^cycleBlockTest)(Block1ViewController *);
    @property(nonatomic,copy) cycleBlockTest testBlock;
    self.testBlock = ^ (Block1ViewController *vc){
            [vc doSomeThing];
        };
        self.testBlock(self);
    
  • 由以上对比可以看出c 函数和 block 匿名函数的区别,以及为什么我们把 block 叫匿名函数

2、为什么说 block 有捕获外部变量的能力(show the code

int a = 10;
    void (^block)() = ^{
        DebugLog(@"a = %d",a);
    };
    a++;
    block();
    DebugLog(@"a = %d",a);   
  • 输出结果分别是:10,11。这是为什么呢?这就是 block 的捕获外部变量的能力。那么 block 是如何捕获的呢,请在本文最后查看如何使用 clang 命令查看 block 内部实现,这里会讲述 block 是如何捕获外部变量。

3、block 在其声明的时候当在 MRC 的环境中使用 copy,当在 ARC 的环境中可使用 copy 也可以使用 strong。目前在 ARC 环境中我们一直还在使用 copy 的原因是延续了 MRC 时代的写法。

4、block 类型我们可以从两方面来说:一、内存分布上;二、声明上。

  • 内存分布上

__NSGlobalBlock 全局 block

void(^block)() = ^{
        DebugLog(@"block");
    };
    block();
    DebugLog(@"%@",block);
    
    block
Block1ViewController.m:60	
<__NSGlobalBlock__: 0x10089c110>

__NSStackBlock 栈 block

int a  = 10;
    void(^block)() = ^{
        DebugLog(@"block-%d",a);
    };
    block();
    DebugLog(@"%@",^{
        DebugLog(@"block-%d",a);
    });
    
    Block1ViewController.m:62	
<__NSStackBlock__: 0x16cf8ba20>

__NSMallocBlock 堆 block

int a  = 10;
    void(^block)() = ^{
        DebugLog(@"block-%d",a);
    };
    block();
    DebugLog(@"%@",block);
    
    block-10
Block1ViewController.m:59	
<__NSMallocBlock__: 0x283394ae0>

  • block声明上

无参数无返回值 void(^block)()

有参数无返回值 void(^block)(int a)

无参数有返回值 int(^block)()

有参数有返回值 int(^block)(int a,int b)

5、如何解决 block 的循环问题(有三种方法show the code)

1、 __weak __strong

__weak typeof(self)weakSelf = self;
    self.testBlock = ^{
        __strong typeof(weakSelf)strongSelf = weakSelf;
        [strongSelf doSomeThing];
    };
    self.testBlock();

2、__block

__block Block1ViewController *vc = self;
    self.testBlock = ^{
        [vc doSomeThing];
        vc = nil;
    };
    self.testBlock();

3、self 作为参数

self.testBlock = ^(Block1ViewController *vc){
        [vc doSomeThing];
    };
    self.testBlock(self);

6 __block修改局部变量 clang 查看内部实现 可以查看下图以及本文全部整理都在图中。