zoukankan      html  css  js  c++  java
  • Block小结

      Blocks是C语言的扩充功能。用一句话来表示Blocks的扩充功能:带有自动变量(局部变量)的匿名函数

      block其实是一个代码块,但是它的神奇之处在于在内联(inline)执行的时候(这和C++很像)还可以传递参数。同时block本身也可以被作为参数在方法和函数间传递,这就给予了block无限的可能。

      Block一般有如下几种使用情况:

    1、作为一个本地变量(local variable)

      returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};

    2、作为@property

      @property (nonatomic, copy) returnType (^blockName)(parameterTypes); 

    3、作为方法的参数(method parameter)

      - (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;

    4、作为方法参数的时候被调用

      [someObject someMethodThatTakesABlock: ^returnType (parameters) {...}];

    5、使用typedef来定义block,可以事半功倍

      typedef returnType (^TypeName)(parameterTypes);
      TypeName blockName = ^returnType(parameters) {...};

      Block与C语言的函数指针是类似的

    //C的函数
    //格式:返回值 方法名 (参数...)
    //1.返回值是void 没有参数的C函数 void cfun (void){ //调用时才执行函数里面的内容 printf("this is a c method "); } //2.返回值是void 带两个参数的C函数 void cfun1(int a,int b){ printf("%d ",a+b); } //3.有返回值 带两个参数的C函数 int cfun2(int c,int d){ int muli = c*d; return muli; } -(void)testC{ cfun(); cfun1(2, 3); int muli = cfun2(4, 5); NSLog(@"乘积是%d",muli); //1.声明一个返回值是void,不带参数的函数指针 //格式:返回值(*xxx)(参数...) void (*fun1)(void); fun1 = &cfun; //函数名本身就是一个地址,&可加可不加 fun1(); //2.声明一个返回值是void,带两个参数的函数指针 void (*fun2)(int a,int b); fun2 = cfun1; fun2(6,7); //3.声明一个返回值是int,带两个参数的函数指针 int (*fun3)(int a,int b); fun3 = cfun2; NSLog(@"%d",fun3(8,9)); }

      下面我们来看怎么定义Block

      1.无参数无返回值的Block

    /**
         *  void :就是无返回值
         *  emptyBlock:就是该 bloc k的名字
         *  ():这里相当于放参数。由于这里是无参数,所以就什么都不写
         */
        void (^emptyBlock)() = ^(){
            NSLog(@"无参数,无返回值的Block");
        };
        emptyBlock();
    

      2.有参数无返回值的Block

    /**
         *  调用这个block进行两个参数相加
         *
         *  @param int 参数A
         *  @param int 参数B
         *
         *  @return 无返回值
         */
        void (^sumBlock)(int ,int ) = ^(int a,int b){
            NSLog(@"%d + %d = %d",a,b,a+b);
        };
        /**
         *  调用这个sumBlock的Block,得到的结果是20
         */
        sumBlock(10,10);

      3.有参数有返回值的Block

      /**
         *  有参数有返回值
         *
         *  @param NSString 字符串1
         *  @param NSString 字符串2
         *
         *  @return 返回拼接好的字符串3
         */    
        NSString* (^logBlock)(NSString *,NSString *) = ^(NSString * str1,NSString *str2){
            return [NSString stringWithFormat:@"%@%@",str1,str2];
        };
        //调用logBlock,输出的是 我是Block
        NSLog(@"%@", logBlock(@"我是",@"Block"));

      那么block与函数指针有什么区别呢

      首先:函数指针是对一个函数地址的引用,这个函数在编译的时候就已经确定了。而block是一个函数对象,是在程序运行过程中产生的。在一个作用域中生成的block对象分配在栈(stack)上,和其他所有分配在栈上的对象一样,离开这个作用域,就不存在了。Block允许开发者在两个对象之间将任意的语句当做数据进行传递,往往这要比引用定义在别处的函数直观。

      其次:blocks是inline的,并且它对局部变量是只读的。

      修改 block 之外的变量

      默认情况下,在程序块中访问的外部变量是复制过去的,即写操作不对原变量生效。比如下面的语句:

    - (void)viewDidLoad
    {
        //将Block定义在方法内部
        int x = 100;
        void (^sumXAndYBlock)(int) = ^(int y){
        x = x+y;
        printf("new x value is %d",x);
        };
        sumXAndYBlock(50);
    }

      这段代码有什么问题呢,Xcode会提示x变量错误信息:Variable is not assigning (missing __block type),这时候给int x = 100;语句前面加上__block关键字即可,如下:

      __block int x = 100;//这样在Block的{}体内,就可以修改外部变量了。

      Block循环引用

      刚刚说过,block在iOS开发中被视作是对象,因此其生命周期会一直等到持有者的生命周期结束了才会结束。另一方面,由于block捕获变量的机制,使得持有block的对象也可能被block持有,从而形成循环引用,导致两者都不能被释放:

    @interface ViewController (){
       void (^_cycleReferenceBlock)(void);
    }
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad { 
       [super viewDidLoad]; 
        _cycleReferenceBlock = ^{ 
          NSLog(@"%@", self); 
          //引发循环引用 
         }; 
      }

      遇到这种代码编译器只会告诉你存在警告,很多时候我们都是忽略警告的,这最后会导致内存泄露,两者都无法释放。跟普通变量存在__block关键字一样的,系统提供给我们__weak的关键字用来修饰对象变量,声明这是一个弱引用的对象,从而解决了循环引用的问题:

      __weak typeof(*&self) weakSelf = self;
      _cycleReferenceBlock = ^{ 
          NSLog(@"%@", weakSelf);   //弱指针引用,不会造成循环引用
      };

      Block作为property属性实现页面之间传值

      这里举例一个Block回调修改上一下界面的背景颜色。控制器1跳转到控制器2,然后在控制器2触发事件回调修改控制器1的背景颜色为红色。

      VC2的实现

    #import <UIKit/UIKit.h>
    /**
     *  定义了一个changeColor的Block。这个changeColor必须带一个参数,这个参数的类型必须为id类型的
     *  无返回值
     *  @param id
     */
    typedef void(^changeColor)(id);
    @interface ViewController2 : UIViewController 

    /**
    * 用上面定义的changeColor声明一个Block,声明的这个Block必须遵守声明的要求。
    */
    @property (nonatomic, copy) changeColor backgroundColor;

    @end
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        //声明一个颜色
        UIColor *color = [UIColor redColor];
        //用刚刚声明的那个Block去回调修改上一界面的背景色
        self.backgroundColor(color);
    }

      VC1的实现

    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        ViewController2 *vc =[[ViewController2 alloc]init];
        // 回调修改颜色
        vc.backgroundColor = ^(UIColor *color){
            self.view.backgroundColor = color;
        };
        [self.navigationController pushViewController:vc animated:YES];
    }

     查看demo

  • 相关阅读:
    springboot中MongoDB的使用
    SpringBoot中发送邮件服务
    springboot中使用RabbitMq
    PostgreSQL学习之【用户权限管理】说明
    Leet Code 19.删除链表的倒数第N个节点
    Kafka集群搭建
    Leet Code 18.四数之和
    Hadoop集群运行情况
    完全分布式Hadoop集群搭建
    Leet Code 17.电话号码的字母组合
  • 原文地址:https://www.cnblogs.com/dongliu/p/5421083.html
Copyright © 2011-2022 走看看