文档章节

Block代码块回调

BookSun
 BookSun
发布于 2016/02/17 19:47
字数 1910
阅读 4
收藏 0
  • Block代码块回调

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

    • 代码块机制

    • Block变量类型

    • Block代码封装及调用

    • Block变量对普通变量作用域的影响

    • Block回调接口使用

      1,代码块机制

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

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

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

      2,Block变量类型

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

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

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

      • 储存代码的返回值类型

      • 储存代码的参数列表

        只要这两个因素一样,我们就可以说是相同的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变量进行储存。封装代码的过程中要注意一下几点:


本文转载自:http://www.2cto.com/kf/201506/404384.html

BookSun
粉丝 0
博文 12
码字总数 2513
作品 0
郑州
程序员
私信 提问
Hyperledger Fabric Node.js如何使用基于通道的事件服务

本教程说明了基于通道的事件的使用。这些事件与现有事件类似,但是特定于单个通道。在设置侦听器时,客户端处理基于通道的事件有一些新选项。从v1.1开始,基于通道的事件是Hyperledger Fabri...

geek12345
04/22
0
0
Objective-C Block与函数指针比较

相似点 1.函数指针和Block都可以实现回调的操作,声明上也很相似,实现上都可以看成是一个代码片段。 2.函数指针类型和Block类型都可以作为变量和函数参数的类型。(typedef定义别名之后,这...

鸿鹄当高远
2018/09/21
0
0
block本质探寻四之copy

说明: <1>阅读本文,最好阅读之前的block文章加以理解; <2>本文内容:三种block类型的copy情况(MRC)、是否深拷贝、错误copy; 一、MRC模式下,三种block类型的copy情况 //代码 void tes...

春天里的花骨朵
01/14
0
0
block语法

一、block应用范围 1、动画的转场 2、网络的事件处理 3、两个对象或者多个对象之间的通讯 4、多线程的处理 (从iOS4.0开始就应用得非常广泛) 二、Block block和C语言中的函数(函数指针)有...

Cy_Star
2017/11/22
0
0
OC中的block与Swift中的尾随闭包的使用

girl.png   OC中的block与Swift中的尾随闭包都起到了将参数作为返回值的作用,也就是常说的回调。 1. block的使用   在OC中block主要分为三种,分别是 (1)NSConcreteGlobalBlock 全局静...

小时候De_我
2017/11/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

读书replay《maven实战》.1.20190526

前情提要 maven这个工具用了好久了,但是一直都用的迷迷糊糊的,没有对它进行过系统性的学习,只是知道一些常用的功能怎么实现,所以20190516这一天我从JD购买了徐晓斌老师所著的《maven实战...

wanxiangming
33分钟前
0
0
真实项目案例实战——【状态设计模式】使用场景

什么是状态模式 状态模式允许一个对象在其内部状态改变的时候改变其行为。这个对象看上去就像是改变了它的类一样。 状态模式应用场景 1.一个对象的行为取决于它的状态,并且它必须在运行时刻根...

须臾之余
40分钟前
1
0
Java 实现把字符串转换成整数【底层实现】

https://blog.csdn.net/zl18310999566/article/details/80263396

qimh
43分钟前
0
0
IDEA的debugger

1、win下节省内存空间 3、条件断点

一只小青蛙
54分钟前
3
0
炸!亿级数据DB秒级平滑扩容

一步一步,娓娓道来。 一般来说,并发量大,吞吐量大的互联网分层架构是怎么样的? 数据库上层都有一个微服务,服务层记录“业务库”与“数据库实例配置”的映射关系,通过数据库连接池向数据...

编程SHA
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部