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

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

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

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

    1,代码块机制

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

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

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

    2,Block变量类型

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

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

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

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

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

      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类型的属性即可。当状态改变时,封装的代码便被组件调用。

代码块(Block)回调一般阐述的更多相关文章

  1. 04OC之分类Category,协议Protocol,Copy,代码块block

    一.Protocol协议 我们都知道,在C#有个规范称之为接口,就是规范一系列的行为,事物.在C#中是使用Interface关键字来声明一个接口的,但是在OC中interface是用来声明类,所以用了 ...

  2. [转]iOS代码块Block

    代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量.作为参数.作为返回值,特殊地,Block还可以保存一段代码,在需要 ...

  3. 从C#到Objective-C,循序渐进学习苹果开发(4)--代码块(block)和错误异常处理的理解

    本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.本文继续上一篇随笔<从 ...

  4. Objective-C-----协议protocol,代码块block,分类category

    概述 ObjC的语法主要基于smalltalk进行设计的,除了提供常规的面向对象特性外,还增加了很多其他特性,本文将重点介绍objective-C中一些常用的语法特性. 当然这些内容虽然和其他高级语言 ...

  5. 代码块(block)的使用

    Objective-C语法之代码块(block)的使用 代码块本质上是和其他变量类似.不同的是,代码块存储的数据是一个函数体.使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值. 脱 ...

  6. 一篇文章看懂iOS代码块Block

    block.png iOS代码块Block 概述 代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量.作为参数.作为返 ...

  7. iOS学习之代码块(Block)

    代码块(Block) (1)主要作用:将一段代码保存起来,在需要的地方调用即可. (2)全局变量在代码块中的使用: 全局变量可以在代码块中使用,同时也可以被改变,代码片段如下: ;//注意:全局变量 ...

  8. Objective-C 代码块(block)的使用

    代码块本质上是和其他变量类似.不同的是,代码块存储的数据是一个函数体.使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值. 脱字符(^)是块的语法标记.按照我们熟悉的参数语法规约所定 ...

  9. Objective-C语法之代码块(block)的使用

    代码块本质上是和其它变量相似.不同的是,代码块存储的数据是一个函数体.使用代码块是,你能够像调用其它标准函数一样,传入參数数,并得到返回值. 脱字符(^)是块的语法标记.依照我们熟悉的參数语法规约所定 ...

随机推荐

  1. Oracle体系结构及备份(十七)——bg-others

    一 其他进程 Archiver (ARCn) Oneor more archiver processes copy the redo log files to archival storage whe ...

  2. 2013 Esri全球用户大会之元数据支持

    1.1 是否支持FGDC CSDGMArcGIS是否支持FGDC CSDGM和North American Profile (NAP)元数据标准?如何支持? 支持,从ArcGIS forDesktop ...

  3. Android监听WebView滑动到底部

    MainActivity如下: package cn.testwebview; import android.app.Activity; import android.graphics.Bitmap; ...

  4. C语言与汇编“硬在哪里”——什么是面向硬件?

    Jack:为什么说C/C++语言是偏向硬件的语言呢? 我:这是把C与java等无指针/引用类编程语言相比较而得出的结论.因为java在j2ee的框架下,写的代码仅仅是逻辑,本质上和写shell脚本没啥 ...

  5. 初识B/S结构编程技术

    B/S结构编程语言 ASP(Active Server Page 动态服务器页面)技术 微软早期推出的B/S编程技术,出现在JSP和ASP.NET之前,PHP当时也很不稳定.ASP之前,动态网站使用G ...

  6. java基础知识拾遗(二)

    1.finally public static int func (){ try{ return 1; }catch (Exception e){ return 2; }finally { retur ...

  7. [日记&做题记录]-Noip2016提高组复赛 倒数十天

    写这篇博客的时候有点激动 为了让自己不颓 还是写写日记 存存模板 Nov.8 2016 今天早上买了两个蛋挞 吃了一个 然后就做数论(前天晚上还是想放弃数论 但是昨天被数论虐了 woc noip模拟赛 ...

  8. Maven之(八)约定优于配置

    maven的配置文件看似很复杂,其实只需要根据项目的实际背景,设置个别的几个配置项而已.maven有自己的一套默认配置,使用者除非必要,并不需要去修改那些约定内容.这就是所谓的"约定优于配置 ...

  9. gridview动态添加行(不用datatable实现)

    GridViewRow newrow = , , DataControlRowType.Separator, DataControlRowState.Normal); TableCell[] tc=] ...

  10. fileupload实现控制大小进行图片上传

    if ($(".img-upload").length > 0) { $('.img-upload').fileupload({ type: 'POST', url: &qu ...