Block的基础使用

2020-05-20  本文已影响0人  E术家

概述

Block的特性

Block的定义和使用

    void(^Myblock1)(void) = ^{
        NSLog(@"无参数,无返回值");
    };
    Myblock1();
    void(^Myblock2)(int a) = ^(int a){
        NSLog(@"%d是我传入的参数,无返回值",a);
    };
    Myblock2(100);
    int(^Myblock3)(int,int) = ^(int a,int b) {
        NSLog(@"%d 和 %d 是我传入的参数,有返回值",a,b);
        return a + b;
    };
    Myblock3(11,12);
    int(^Myblock4)(void) = ^ {
        NSLog(@"无参数,有返回值");
        return 233;
    };
    Myblock4();
typedef int (^SnowBlock) (int,int);

这时 SnowBlock 就成为了一种Block类型
定义属性可以这样

@property (nonatomic, copy) SnowBlock myBlock;

使用时

    self.myBlock = ^int(int a, int b) {
        //TODO
        return a + b;
    };

Block与外界变量

接活自动变量(局部变量)值
    int age = 27;
    void(^AgeBlock)(void) = ^{
        NSLog(@"age = %d",age);
    };
    age = 18
    AgeBlock();

输出结果


    __block int age2 = 27;
    void(^AgeBlock2)(void) = ^{
        NSLog(@"age2 = %d",age2);
    };
    age2 = 18;
    AgeBlock2();

输出结果


Block的copy操作

简而言之,存储在栈中的Block就是栈块、存储在堆中的就是堆块、既不在栈中也不在堆中的块就是全局块
遇到一个Block,我们怎么确定这个Block的存储位置 ?

(1)Block不访问外界变量(包括栈中和堆中的变量)
Block既不在栈又不在堆中,在代码段中,ARC和MRC下都是如此。此时为全局块。
(2)Block访问外界变量
MRC环境下:访问外界变量的Block默认存在栈中。
ARC环境下:访问外界变量的Block默认存在堆中(实际是放在栈区,然后ARC情况下自动又拷贝到堆区),自动释放。

ARC下,访问外界变量的Block为什么要自动从栈区拷贝到堆区?

栈上的Block,如果其所属的变量作用域结束,该Block就被废弃,如同一般的自动变量。当然,Block中的__block变量也同时废弃。



为了解决栈块在其变量作用域结束之后被废弃(释放)的问题,我们需要把Block复制到堆中,延长其生命周期。在开启ARC时,大多数情况下编译器会恰当地进行判断是否需要将Block从栈复制到堆,如果有,自动生成将Block从栈复制到堆的代码。

Block的复制操作执行的是copy实例方法。Block只要调用了copy方法,栈块就会变成堆块。

在非ARC情况下需要开发者调用copy方法手动复制,由于开发中几乎都是ARC模式,所以手动复制内容不再过多研究。

将Block从栈上复制到堆上相当消耗CPU,所以当Block设置在栈上也能够使用时,就不要复制了,因为此时的复制是在浪费CPU资源。

Block的复制操作执行的是copy实例方法。不同类型的Block使用copy方法如下


根据表得知,Block在堆中copy会造成引用计数增加,这与其他Objectice-C对象是一样的。虽然Block在栈中也是以对象的身份存在,但是栈块没有引用计数,因为不需要,栈区的内存编译器自动分配释放。

不管Block存储域在何处,用copy方法复制都不会引起任何问题。在不确定时调用copy方法即可。

在ARC有效是,多次调用copy方法完全没有问题

__forwarding

通过__forwarding,无论是在block中还是block外访问的__block变量,也不管该变量在栈上或堆上,都能顺利地访问同一个__block变量

防止 Block 循环引用

Block 循环引用的情况
某个类将block作为自己的属性变量,然后再block的方法体里又使用了该类本身

    self.myBlock = ^int(int a, int b) {
        [self dosomething];
        return a + b;
    };

解决办法

    __weak typeof(self) weakSelf = self;
    self.myBlock = ^int(int a, int b) {
        [weakSelf dosomething];
        return a + b;
    };
    __block typeof(self) blockSelf = self;
    self.myBlock = ^int(int a, int b) {
        [blockSelf dosomething];
        return a + b;
    };

值得注意的是,在ARC下,使用 __block 也有可能带来循环引用。

Block 使用示例

    int (^sum) (int,int); //定义一个 Block 变量 sum
    //给 Block 变量赋值
    //一般 返回值省略
    sum = ^int (int a,int b) {
        return a + b;
    };
    int n = sum(10,12);// 调用
    // 给 Calculate 类型 sum2变量 赋值
    typedef int (^Calculate)(int,int); //Calculate 就是类型名
    Calculate sum2 = ^(int a,int b) {
        return a + b;
    };
    int a = sum2(10,20);//调用 sum2 变量
typedef int (^SnowBlock) (int,int);
//作为对象的属性声明,copy后block会转移到堆中和对象一起
@property (nonatomic, copy) SnowBlock myBlock; //使用 typedef
@property (nonatomic, copy) int (^sumNumber)(int,int); //不使用 typedef
    self.sumNumber = ^int(int a, int b) {
        return a + b;
    };
- (CGFloat)testTimeConsume:(void(^)(void))middleBlock {
    // 执行前记录下当前的时间
    CFTimeInterval startTime = CACurrentMediaTime();
    middleBlock();
    // 执行后记录下当前时间
    CFTimeInterval endTime = CACurrentMediaTime();
    return endTime - startTime;
}

调用

    [self testTimeConsume:^{
       // 放入 block 中的代码
    }];

有参数传递的 block

- (CGFloat)testTimeConsume2:(void(^)(NSString *name))middleBlock {
    // 执行前记录下当前的时间
    CFTimeInterval startTime = CACurrentMediaTime();
    NSString *name = @"有参数";
    middleBlock(name);
    // 执行后记录下当前时间
    CFTimeInterval endTime = CACurrentMediaTime();
    return endTime - startTime;
}

调用

    [self testTimeConsume2:^(NSString *name) {
        // 放入 block 中的代码 可以使用参数 name
        // 参数 name 是实现代码中传入的 在调用时只能使用 不能传值
    }];

核心代码
A界面中 .m button点击触发函数

- (void)gotoB {
    BlockExampleB *vcB = [BlockExampleB new];
    [self.navigationController pushViewController:vcB animated:YES];
    
    __weak typeof(self) weakSelf = self;
    [vcB setCVBlock:^(NSString * string) {
        weakSelf.textLabel.text = string;
    }];
}

B界面中 .h

typedef void(^getVCBstringBlock) (NSString *string);
@property (nonatomic, copy) getVCBstringBlock CVBlock;

.m 中

- (void)back {
    [self.navigationController popViewControllerAnimated:YES];
    self.CVBlock(self.textField.text);
}

相关调试demo:https://github.com/Snowxls/BlockKnowledge

上一篇下一篇

猜你喜欢

热点阅读