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

  • 代码块机制
  • 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. Linux进程分配内存的两种方式--brk() 和mmap()

    如何查看进程发生缺页中断的次数? 用ps -o majflt,minflt -C program命令查看. majflt代表major fault,中文名叫大错误,minflt代表minor faul ...

  2. js jquery 实现html页面之间参数传递(单一参数、对象参数传递)

    最近自己在忙着做毕业设计,后台程序员,前端菜鸡,因为需要,所以实现了html页面之间参数传递.------jstarseven .菜鸡的自我修养. 页面A代码如下: <!DOCTYPE html ...

  3. C#简单验证并限制登录次数小示例

    描述:提示用户输入用户名,密码,与数据库中的数据匹配,如果正确则提示登陆成功,否则,提示登陆失败 错误次数超过三次,提示登陆次数过多,禁止登陆 数据表示例: //数据库中ErrorTimes的次数增加 ...

  4. 机器学习( Machine Learning)的定义

    关于机器学习有两个相关的定义: 1)给计算机赋予没有固定编程的学习能力的研究领域. 2)一种计算机的程序,能从一些任务(T)和性能的度量(P),经验(E)中进行学习.在学习中,任务T的性能P能够随着P ...

  5. 微信小程序开发系列(一)小程序开发初体验

    开发小程序所需的基本技能   关于小程序的介绍和使用场景这里不作介绍,这个系列的文章会一步一步地带领大家快速地学习和掌握小程序的开发. 关于还没有接触过小程序的开发者来说,最关心的问题无非就是,开发小 ...

  6. NG2入门 - 架构

    AngularJS2 学习 继TypeScript之后,终于到了ng2的学习路程,同样学习根据angular官网文档进行,对文档中的内容根据自己的理解略有改动.看官可看官网文档,也可以看本系列博文 首 ...

  7. 009-程序集路径Web窗体

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs& ...

  8. [UWP小白日记-4]记账项目-2

    手机端: 待续…… 哈哈这个代码真是好长时间啊,没办法萌新你们都懂的,UI是改了又改,知识也在慢慢积累, 所以这效率就低下了点 UI是改了又改,代码是不断了改,所以搞到现在都没开发完成

  9. Python反射函数

    python里面跟getattr相关的有hasattr,setattr,delattr  ,那么我们通过下面的例子,来详细的说说他们的用法. class Xiaorui: def __init__(s ...

  10. Aspose系列实现docx转PDF,PPT转PDF,EXCEL转PDF

    没有什么营养,就是调用一下这个组件.其实一开始用的是Microsoft.Office.Interop.Excel;Microsoft.Office.Interop.Word 但是在服务器要注意,服务器 ...