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

  • 相关阅读:
    java io系列23之 BufferedReader(字符缓冲输入流)
    java io系列22之 FileReader和FileWriter
    java io系列21之 InputStreamReader和OutputStreamWriter
    java io系列20之 PipedReader和PipedWriter
    java io系列19之 CharArrayWriter(字符数组输出流)
    java io系列18之 CharArrayReader(字符数组输入流)
    java io系列17之 System.out.println("hello world")原理
    java io系列16之 PrintStream(打印输出流)详解
    java io系列15之 DataOutputStream(数据输出流)的认知、源码和示例
    java io系列14之 DataInputStream(数据输入流)的认知、源码和示例
  • 原文地址:https://www.cnblogs.com/dongliu/p/5421083.html
Copyright © 2011-2022 走看看