代码块(Block)回调通常阐述

本章教程主要对代码块回调模式进行讲解,已经分析其余回调的各类优缺点和适合的使用场景。html

  • 代码块机制
  • Block变量类型
  • Block代码封装及调用
  • Block变量对普通变量做用域的影响
  • Block回调接口使用
  • 0、Block简介

    Block块是封装工做单元的对象,是能够在任什么时候间执行的代码段。其本质上是可移植的匿名函数,能够做为方法和函数的参数传入,能够从方法和函数中返回。—(翻译自官方文档)java

    块是对C语言的一种扩展,它并未做为标准的ANSI C所定义的部分,而是有苹果公司添加到语言中的。块看起来更像是函数,能够给块传递参数,块也能够具备返回值。编程

    1,代码块机制

    苹果公司在iOS4 SDK中首次支持代码块机制,随后代码块机制被普遍应用于各类编码场景,最多见的为回调机制,也成为Block回调。函数

    代码块也称Block。是封装代码的一种机制,也能够称为匿名函数。编码

    使用这种机制能够将一段代码放入一个Block变量中进行存储,该变量能够做为参数进行传递,也能够经过该变量调用其存储的代码。atom

    2,Block变量类型

    在OC语法中,建立一个变量首先要明确其类型。Block做为一个能够储存代码的变量,其类型相对特殊。spa

    肯定block变量的类型有两个因素:翻译

    • 储存代码的返回值类型
    • 储存代码的参数列表

      只要这两个因素同样,咱们就能够说是相同的block类型。code

      如今咱们举一个简单的例子,建立一个储存没有返回值,没有输入参数的代码的block类型。htm

      ?
      1
      2
      <code> void (^ varBlock)( void );
      </code>

      上面的代码声明了一个block变量,变量名为varBlock,其储存代码类型为没有返回值,没有输入参数。

      若是想要储存有返回值,有输入参数的代码,一样能够声明响应的block变量进行使用。

      ?
      1
      2
      <code> int (^ varBlock1)( int a, int b);
      </code>

      上面的代码声明了一个block变量,变量名为varBlock1,其储存代码类型为int型返回值,有两个int型参数。

      Block变量类型较为复杂,若是直接用这种方式进行声明变量十分容易储存。一般咱们用typedef关键字将Block类型重命名,而后用相对简单的类型名进行声明变量的工做。

      ?
      1
      2
      3
      4
      <code>typedef void (^ BlockType1)( void );
       
      BlockType1 var1; //var1与varBlock1为同一类型
      </code>

      3,Block代码封装及调用

      有了Block变量,下面咱们就要给变量赋值。

      ?
      1
      2
      3
      4
      5
      6
      <code>typedef void (^ BlockType1)( void );
       
      BlockType1 var1;
       
      var1 = ^(){NSLog(@ "test" )};
      </code>

      经过上述语法格式将代码封装在大括号内,并用var1变量进行储存。封装代码的过程当中要注意一下几点:

      • ^符号开始为Block封装过程。
      • ^后面的小括号中写这段代码须要的参数。该参数有调用者进行赋值。
      • 小括号后面的大括号中写要封装的代码,且代码可使用小括号中的参数。

        下面举一个求两个数的和的代码封装过程。

        ?
        1
        2
        3
        4
        5
        6
        <code>typedef int (^BlockType)( int a, int b);
         
        BlockType varBlock;
         
        varBlock = ^( int a, int b){ return a+b;};
        </code>

        将代码存入varBlock变量中后,即可以使用该变量调用代码。

        ?
        1
        2
        3
        4
        5
        <code> int a = 4 ;
        int b = 6 ;
        int sum = varBlock(a,b);
        NSLog(@ "sum = %d" ,sum); //输出结果为10
        </code>

        Block变量也能够给同类型的变量赋值

        ?
        1
        2
        3
        4
        5
        <code>BlockType varBlockTemp;
        varBlockTemp = varBlock;
        int sum = varBlockTemp( 1 , 2 );
        NSLog(@ "sum = %d" ,sum); //输出结果为3
        </code>

        将一段代码当作一个变量进行传递,Block这样的特性极大的方便了咱们以后的编码工做

        3,Block变量对普通变量做用域的影响

        经过Block对象将代码进行封装的同时,有一个很是关键的问题咱们须要明确讨论,即Block变量对普通变量做用域的影响。

        经过一个简单案例来所以这个问题。见以下代码:

        ?
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        <code>main()
        {
             {
                 int a = 1 ;
                 {
                     a = 2 ;
                 }
                 //此处输出a的值为2
             }
             //此处已经超出变量a的做用域,讨论a的值无心义。
        }
        </code>

        这段代码很简单,经过大扩展来表示变量的做用域范围。再看下面代码:

        ?
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        <code>typedef void (^ BlockType)( void );
         
        BlockType var;
         
        void fun1()
        {
             int a = 10 ;
             var = ^(){NSLog(@ "a = %d" ,a)};
        }
         
        void fun2()
        {
             var();
        }
         
        main()
        {
             fun1();
             fun2();
        }
        </code>

        在fun2函数中调用var变量时,运行的是fun1中存入var变量的代码,且代码中的使用的变量a也是fun1中的局部变量。

        正常状态下,变量a的做用域在fun1函数体大括号内。在函数体大括号外面使用a是没有意义的。

        但此处状况特殊,变量a被block变量var所使用,因此var变量将a进行了一个复制操做,也就是咱们在var的代码里面使用的a实际上是a的副本。

        咱们看下面的代码:

        ?
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        <code>typedef void (^ BlockType)( void );
         
        BlockType var;
         
        void fun1()
        {
             int a = 10 ;
             var = ^(){NSLog(@ "a = %d" ,a)};
             a = 20 ;
        }
         
        void fun2()
        {
             var();
        }
         
        main()
        {
             fun1();
             fun2();
        }
        </code>

        这段代码的输出和上一段代码同样,不会由于fun1函数中a的值发生变化而致使block里面的a的值发生变化。缘由是Block变量在使用局部变量是,会对局部变量进行一个复制操做,block变量中储存的代码使用的时局部变量的副本。

        可是在某些特殊场合,咱们须要改变局部变量能够引发block变量中代码的变化。这时候咱们须要使用block全景变量。

        block全景变量经过:__block来声明。

        ?
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        <code>typedef void (^ BlockType)( void );
         
        BlockType var;
         
        void fun1()
        {
             __block int a = 10 ;
             var = ^(){NSLog(@ "a = %d" ,a)};
             a = 20 ;
        }
         
        void fun2()
        {
             var();
        }
         
        main()
        {
             fun1();
             fun2();
        }
        </code>

        上文代码中,fun1中的变量a被block全景变量标识符所修饰,即变量a成为一个block全景变量。

        其做用是,此时block封装代码时使用a变量,不会进行复制操做,也就表示,局部变量a与block代码中的a为同一个变量。因此,当前代码的运行结果为 a = 20。

        4,Block回调接口使用

        回调的本质为控件反馈自身信息,在面向对象编程中,控件须要调用方法反馈自身信息,而方法必须从属某个对象。因此以前的回调接口必须设置两个参数,一个反馈对象,一个反馈方法。

        • 在目标动做中,反馈对象为target,反馈方法为action,一个SEL类型的变量。
        • 在委托回调中,反馈对象为delegate,反馈方法为组件协议中声明的方法。

          在Block回调中,因Block机制能够直接将代码封装如一个变量中,并且这个变量能够当作参数进行传递。利用这个机制,组件能够保存这段代码,在触发事件的时候直接调用此段代码,不须要设置反馈对象和反馈方法。

          这里仍然用以前的开关最为例子:

          ?
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          <code>typedef enum : NSUInteger {
               SwitchStateOff, //default
               SwitchStateOn,
          } SwitchState;
           
          typedef void (^SBlockType)(SwitchState state);
           
          @interface SwitchB : NSObject
           
          @property (nonatomic,assign,readonly)SwitchState currentState;
           
          @property (nonatomic,strong)SBlockType changeStateBlockHandle;
           
          @end
          </code>

          声明中的changeStateBlockHandle属性就是保存回调代码。设置回调,只须要将此属性赋值就可。

          ?
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          <code> @interface Room : NSObject
          @property (strong, nonatomic) Light *lightA;
          @property (strong, nonatomic) SwitchB *s;
           
          @end
           
           
          @implementation Room
          - (instancetype)init
          {
               self = [ super init];
               if (self) {
                   self.lightA = [[Light alloc] init];
                   self.s = [[SwitchB alloc] init];
           
                   __weak __block Room * copy_self = self; //打破强引用循环,后续章节会展开讲解
           
                   self.s.changeStateBlockHandle = ^(SwitchState state)
                   {
                       if (state == SwitchStateOff)
                       {
                           [self.lightA turnOff];
                       }
                       else
                       {
                           [self.lightA turnOn];
                       }
                   };
               }
               return self;
          }
          @end
          </code>

          当开关的状态发生改变时,开关须要将自身状态反馈给使用者。当使用Block回调接口的组件时,须要将回调代码直接封装,赋值给组件响应的Block类型的属性便可。当状态改变时,封装的代码便被组件调用。

相关文章
相关标签/搜索