欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Block的那些事

程序员文章站 2022-07-12 23:15:38
...

Block是什么

1、block是一种数据类型,所以我们可以声明一个block类型的变量

2、block类型的变量专门用来存储一段代码,这段代码可以有参数,可以有返回值

Block的故事

UIButton就像是一件衣服,一般人会直接不加修饰的穿上,可以实现它作为一件衣服的功能,但是不能突显它真正的漂亮。优秀的人根据自己的理解,进行部门的修改并且还搭配一些小饰品,穿出来的就不仅仅是一件衣服,更像是艺术品。UIButton可以简单用也可以复杂用,关键看你怎么用。

Block的声明方法:

  • block的声明: 必须指定block变量的返回值和参数类型
  • block的声明规范: 返回类型(^block变量名称)(参数列表)
	void (^block1)();                      //无返回值无参数 名为block1的声明
    void (^block2)(int num1);              //无返回值一个参数名为block2的声明
    int (^block3)(int num1, int num2);     //有返回值两个参数名为block3的声明

Block的初始化方法:

  • block的初始化 :写一段符合block要求的代码段进行存储
  • 初始化规范:^返回类型(参数列表){代码块}
  • 初始化的过程中注意block的实现要和block的声明对应,返回值和参数列表一一对应。
    block1 = ^void () {
        NSLog(@"我是无参数无返回值的代码段");
    };
    
    block2 = ^void(int num2){
        num2 = num2 + 1;
        NSLog(@"num2 = %d",num2);
    };
    
    block3 = ^int(int num1, int num2){
        int num = num1 + num2;
        NSLog(@"num = %d",num);
        return  num;
    };

Block的执行:

  • 如同函数调用
 	block1();                    //无返回值无参数的调用
    block2(5);				     //无返回值有参数的调用
    int res = block3(3,4);       //又返回值有参数的调用

Block实现过程的简写:

  • 代码段没有返回值,那么代码段的void可以省略
  • 代码段没有参数,那么代码段的()可以省略
  • 注意:block实现的时候可以省略,声明的过程不符合以上规则
	block1 = ^{
         NSLog(@"我是无参数无返回值的代码段");
    };
    
    block2 = ^(int num2){
        num2 = num2 + 1;
        NSLog(@"num2 = %d",num2);
    };
  • 无论代码段是否有返回值,在实现block的时候,可以不写返回类型,返回类型可以全部省略。(经常使用)
    block1 = ^() {
        NSLog(@"我是无参数无返回值的代码段");
    };
    
    block2 = ^(int num2){
        num2 = num2 + 1;
        NSLog(@"num2 = %d",num2);
    };
    
    block3 = ^(int num1, int num2){           //省略返回类型 int
        int num = num1 + num2;
        NSLog(@"num = %d",num);
        return  num;
    };
  • 如果不写返回类型,系统会自动确定返回类型,通过判断block内部是否有return语句,如有多个return语句,必须返回的都是同一个类型的数据。如果没有return语句就默认void
    int(^block4)(int,int) = ^(int num1, int num2){

        if(num1 > num2){
            return 1;
        }
        else{
            return 2.0;  //这里会报错 改为2即可
        }
    };
 

使用typedef简写Block声明:

	typedef void(^TypeBlock)();    
    TypeBlock block4 = block1;
    block4();
    
    typedef void(^TypeBlock2)(int);
    TypeBlock2 block5 = block2;
    block5(5);
    
    typedef int(^TypeBlock3)(int,int);
    TypeBlock3 block6 = block3;
    block6(3,4);

block内部访问外部变量:

  • 1、在block代码块内部可以访问定义在外部的变量的值,定义在外部的局部变量和全局变量
  • 2、在block代码块的内部可以修改全局变量的值,但是不能修改定义在外部的局部变量的值
  • 3、如果要修改block代码块外部定义的局部变量的值,那就为这个局部变量加上__block
    int number1 = 100;
    __block int number3 = 300;
    TypeBlock block7 = ^(){
        int number2 = 200;
        //number1++;             加上此句会报错,因为不能修改block块外部局部变量的值
        number2++;
        number3++;
        NSLog(@"num = %d ",number1);
        NSLog(@"num = %d ",number2);
        NSLog(@"num = %d ",number3);
    };
    block7();

block作为函数参数:

  • 可以将调用者自己写的代码传递到函数的内部执行
          typedef void(^TypeBlock)();
          void test(TypeBlock block)    //定义block为参数的两个函数
          {
              NSLog(@"------");
              block();
              NSLog(@"------");
          }

          typedef int(^TypeBlock3)(int,int);
          int test2(TypeBlock3 block)
          {
              NSLog(@"+++++++");
              int res = block(3,4);
              NSLog(@"+++++++");
              return res;
          }
    
  	test(block1);        //block1是上面声明实现过的 block1是先初始化完成后再调用的
    test(^{              
        NSLog(@"我是参数block");  //参数内容直接自己写,两种方式均可; 第二种偏多。
    });                              
    
    test2(block3);      //block3也是上面实现过的
    test2(^int(int num1, int num2) {
        int res = num1 + num2;
        NSLog(@"res = %d",res);
        return res;
    });
    
  • block 作为函数参数,让调用者参与函数方法内部的实现,下面我们来体会一下

我们创建一个类,里面有一个数组,添加一个初始化的方法和一个遍历的方法

@interface Array : NSObject
{
    int _arr[10]; 
    
}
- (instancetype)init;
- (void)Traverse : (void(^)(int val)) block;

@end
    

类的实现:

@interface Array : NSObject
{
    int _arr[10]; 
    
}
- (instancetype)init;
- (void)Traverse : (void(^)(int val)) block;

@end
    
#import "Array.h"

@implementation Array

- (instancetype)init
{
    if (self = [super init])
    {
        for (int i = 1; i < 11; i++)
        {
            _arr[i-1] = i * 10;  //给数组内元素赋值 
        }
    }
    return self;
}
- (void)Traverse : (void(^)(int val)) block
{
    for (int i = 0; i < 10; i++)
    {
        block(_arr[i]);      //让调用者自己通过block来控制对数组元素的操作
    }
}
@end
    

调用者:

记得先#import "Array.h"

然后在主函数中:
    Array *arr = [Array new];
    [arr Traverse:^(int val) {
        if (val > 50)               //调用者自己写对数组元素的操作。可以任意修改以满足需求。
            NSLog(@"%d",val);
    }];
    
  • 是不是感觉这个函数活了,调用者可以自己去控制函数内部的实现过程,每次主要改动block就可以实现新的操作
  • 之前我们之前直接调用,然后遍历数组并输出,有任何改动要需要重写函数

block作为函数返回值

  • block作为函数返回值必须使用typedef的方式,否则无法编译通过
 //错误方法:
      void(^)() test4()
      {
          TypeBlock block1;
          return block1;
      }
//正确方法:

      typedef void(^TypeBlock)();
      TypeBlock test3()
      {
          return ^{
              NSLog(@"block作为函数返回参数可以使用");
          };
      }
    

Block的基础学习至此先告一段落,这些目前可以简单使用了,老铁你会了吗。