c/c++语言开发共享OC教程6-代码块block回调

oc6-代码块回调 本章教程主要对代码块回调模式进行讲解,已经分析其他回调的各种优缺点和适合的使用场景。 代码块机制 block变量类型 block代码封装及调用 block变量对普通变量作用域的


oc6-代码块回调

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

  • 代码块机制
  • block变量类型
  • block代码封装及调用
  • block变量对普通变量作用域的影响
  • block回调接口使用

    1,代码块机制

    苹果公司在ios4 sdk中首次支持代码块机制,随后代码块机制被广泛应用于各种编码场景,最常见的为回调机制,也成为block回调。

    代码块也称block。是封装代码的一种机制,也可以称为匿名函数。

    使用这种机制可以将一段代码放入一个block变量中进行存储,该变量可以作为参数进行传递,也可以通过该变量调用其存储的代码。

    2,block变量类型

    在oc语法中,创建一个变量首先要明确其类型。block作为一个可以储存代码的变量,其类型相对特殊。

    确定block变量的类型有两个因素:

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

      只要这两个因素一样,我们就可以说是相同的block类型。

      现在我们举一个简单的例子,创建一个储存没有返回值,没有输入参数的代码的block类型。

        void (^ varblock)(void);  

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

      如果想要储存有返回值,有输入参数的代码,同样可以声明响应的block变量进行使用。

        int (^ varblock1)(int a,int b);  

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

      block变量类型较为复杂,如果直接用这种方式进行声明变量十分容易储存。通常我们用typedef关键字将block类型重命名,然后用相对简单的类型名进行声明变量的工作。

        typedef void (^ blocktype1)(void);    blocktype1 var1;//var1与varblock1为同一类型  

      3,block代码封装及调用

      有了block变量,下面我们就要给变量赋值。

        typedef void (^ blocktype1)(void);    blocktype1 var1;    var1 = ^(){nslog(@"test")};  

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

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

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

          typedef int (^blocktype)(int a,int b);    blocktype varblock;    varblock = ^(int a,int b){return a+b;};  

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

          int a = 4;  int b = 6;  int sum = varblock(a,b);  nslog(@"sum = %d",sum);//输出结果为10  

        block变量也可以给同类型的变量赋值

          blocktype varblocktemp;  varblocktemp = varblock;  int sum = varblocktemp(1,2);  nslog(@"sum = %d",sum);//输出结果为3  

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

        3,block变量对普通变量作用域的影响

        通过block对象将代码进行封装的同时,有一个非常关键的问题我们需要明确讨论,即block变量对普通变量作用域的影响。

        通过一个简单案例来因此这个问题。见如下代码:

          main()  {      {          int a = 1;          {              a = 2;          }          //此处输出a的值为2      }      //此处已经超出变量a的作用域,讨论a的值无意义。  }  

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

          typedef void (^ blocktype)(void);    blocktype var;    void fun1()  {      int a = 10;      var = ^(){nslog(@"a = %d",a)};  }    void fun2()  {      var();  }    main()  {      fun1();      fun2();  }  

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

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

        但此处情况特殊,变量a被block变量var所使用,所以var变量将a进行了一个复制操作,也就是我们在var的代码里面使用的a其实是a的副本。

        我们看下面的代码:

          typedef void (^ blocktype)(void);    blocktype var;    void fun1()  {      int a = 10;      var = ^(){nslog(@"a = %d",a)};      a = 20;  }    void fun2()  {      var();  }    main()  {      fun1();      fun2();  }  

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

        但是在某些特殊场合,我们需要改变局部变量可以引起block变量中代码的变化。这时候我们需要使用block全景变量。

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

          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();  }  

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

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

        4,block回调接口使用

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

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

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

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

            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  

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

            @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  

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

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/606545.html

(0)
上一篇 2021年5月14日
下一篇 2021年5月14日

精彩推荐