代码块(Block)回调一般阐述
本章教程主要对代码块回调模式进行讲解,已经分析其他回调的各种优缺点和适合的使用场景。
- 代码块机制
- Block变量类型
- Block代码封装及调用
- Block变量对普通变量作用域的影响
- Block回调接口使用
0、Block简介
Block块是封装工作单元的对象,是可以在任何时间执行的代码段。其本质上是可移植的匿名函数,可以作为方法和函数的参数传入,可以从方法和函数中返回。—(翻译自官方文档)
块是对C语言的一种扩展,它并未作为标准的ANSI C所定义的部分,而是有苹果公司添加到语言中的。块看起来更像是函数,可以给块传递参数,块也可以具有返回值。
1,代码块机制
苹果公司在iOS4 SDK中首次支持代码块机制,随后代码块机制被广泛应用于各种编码场景,最常见的为回调机制,也成为Block回调。
代码块也称Block。是封装代码的一种机制,也可以称为匿名函数。
使用这种机制可以将一段代码放入一个Block变量中进行存储,该变量可以作为参数进行传递,也可以通过该变量调用其存储的代码。
2,Block变量类型
在OC语法中,创建一个变量首先要明确其类型。Block作为一个可以储存代码的变量,其类型相对特殊。
确定block变量的类型有两个因素:
- 储存代码的返回值类型
- 储存代码的参数列表
只要这两个因素一样,我们就可以说是相同的block类型。
现在我们举一个简单的例子,创建一个储存没有返回值,没有输入参数的代码的block类型。
12<code>
void
(^ varBlock)(
void
);
</code>
上面的代码声明了一个block变量,变量名为
varBlock
,其储存代码类型为没有返回值,没有输入参数。如果想要储存有返回值,有输入参数的代码,同样可以声明响应的block变量进行使用。
12<code>
int
(^ varBlock1)(
int
a,
int
b);
</code>
上面的代码声明了一个block变量,变量名为
varBlock1
,其储存代码类型为int型返回值,有两个int型参数。Block变量类型较为复杂,如果直接用这种方式进行声明变量十分容易储存。通常我们用
typedef
关键字将Block类型重命名,然后用相对简单的类型名进行声明变量的工作。1234<code>typedef
void
(^ BlockType1)(
void
);
BlockType1 var1;
//var1与varBlock1为同一类型
</code>
3,Block代码封装及调用
有了Block变量,下面我们就要给变量赋值。
123456<code>typedef
void
(^ BlockType1)(
void
);
BlockType1 var1;
var1 = ^(){NSLog(@
"test"
)};
</code>
通过上述语法格式将代码封装在大括号内,并用
var1
变量进行储存。封装代码的过程中要注意一下几点:- 以
^
符号开始为Block封装过程。 ^
后面的小括号中写这段代码需要的参数。该参数有调用者进行赋值。- 小括号后面的大括号中写要封装的代码,且代码可以使用小括号中的参数。
下面举一个求两个数的和的代码封装过程。
123456<code>typedef
int
(^BlockType)(
int
a,
int
b);
BlockType varBlock;
varBlock = ^(
int
a,
int
b){
return
a+b;};
</code>
将代码存入
varBlock
变量中后,便可以使用该变量调用代码。12345<code>
int
a =
4
;
int
b =
6
;
int
sum = varBlock(a,b);
NSLog(@
"sum = %d"
,sum);
//输出结果为10
</code>
Block变量也可以给同类型的变量赋值
12345<code>BlockType varBlockTemp;
varBlockTemp = varBlock;
int
sum = varBlockTemp(
1
,
2
);
NSLog(@
"sum = %d"
,sum);
//输出结果为3
</code>
将一段代码当做一个变量进行传递,Block这样的特性极大的方便了我们之后的编码工作
3,Block变量对普通变量作用域的影响
通过Block对象将代码进行封装的同时,有一个非常关键的问题我们需要明确讨论,即Block变量对普通变量作用域的影响。
通过一个简单案例来因此这个问题。见如下代码:
123456789101112<code>main()
{
{
int
a =
1
;
{
a =
2
;
}
//此处输出a的值为2
}
//此处已经超出变量a的作用域,讨论a的值无意义。
}
</code>
这段代码很简单,通过大扩展来表示变量的作用域范围。再看下面代码:
123456789101112131415161718192021<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的副本。我们看下面的代码:
12345678910111213141516171819202122<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
来声明。12345678910111213141516171819202122<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机制可以直接将代码封装如一个变量中,而且这个变量可以当做参数进行传递。利用这个机制,组件可以保存这段代码,在触发事件的时候直接调用此段代码,不需要设置反馈对象和反馈方法。
这里仍然用之前的开关最为例子:
123456789101112131415<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
属性就是保存回调代码。设置回调,只需要将此属性赋值就可。123456789101112131415161718192021222324252627282930313233<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)回调一般阐述的更多相关文章
- 04OC之分类Category,协议Protocol,Copy,代码块block
一.Protocol协议 我们都知道,在C#有个规范称之为接口,就是规范一系列的行为,事物.在C#中是使用Interface关键字来声明一个接口的,但是在OC中interface是用来声明类,所以用了 ...
- [转]iOS代码块Block
代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量.作为参数.作为返回值,特殊地,Block还可以保存一段代码,在需要 ...
- 从C#到Objective-C,循序渐进学习苹果开发(4)--代码块(block)和错误异常处理的理解
本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.本文继续上一篇随笔<从 ...
- Objective-C-----协议protocol,代码块block,分类category
概述 ObjC的语法主要基于smalltalk进行设计的,除了提供常规的面向对象特性外,还增加了很多其他特性,本文将重点介绍objective-C中一些常用的语法特性. 当然这些内容虽然和其他高级语言 ...
- 代码块(block)的使用
Objective-C语法之代码块(block)的使用 代码块本质上是和其他变量类似.不同的是,代码块存储的数据是一个函数体.使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值. 脱 ...
- 一篇文章看懂iOS代码块Block
block.png iOS代码块Block 概述 代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量.作为参数.作为返 ...
- iOS学习之代码块(Block)
代码块(Block) (1)主要作用:将一段代码保存起来,在需要的地方调用即可. (2)全局变量在代码块中的使用: 全局变量可以在代码块中使用,同时也可以被改变,代码片段如下: ;//注意:全局变量 ...
- Objective-C 代码块(block)的使用
代码块本质上是和其他变量类似.不同的是,代码块存储的数据是一个函数体.使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值. 脱字符(^)是块的语法标记.按照我们熟悉的参数语法规约所定 ...
- Objective-C语法之代码块(block)的使用
代码块本质上是和其它变量相似.不同的是,代码块存储的数据是一个函数体.使用代码块是,你能够像调用其它标准函数一样,传入參数数,并得到返回值. 脱字符(^)是块的语法标记.依照我们熟悉的參数语法规约所定 ...
随机推荐
- 读excel时候出现java内存溢出
修改Eclipse,或MyEclipse的内存 例如MyEclipse 在window->preferences->myeclipse->application server-> ...
- Objective-C非正式协议与正式协议
这两个概念困扰我很久了,一直都很像搞清楚到非正式协议和正式协议有什么区别和联系,下面结合网上的资料和自己的看法谈谈这个问题. 一.非正式协议 显然这个名词是相对于正式协议而言的.在解释非正式协议之前, ...
- Android本地化资源目录详解
我们可以设想,有两个不同分辨率的手机(320*480和480*800)要使用一些图像资源,为了使图像不失真,就需要为不同分辨率的手机指定不同的图像,为此就需要建立不同的资源目录. 在res目录中建立了 ...
- twemproxy接收流程探索——twemproxy代码分析正编
在这篇文章开始前,大家要做好一个小小的心理准备,由于twemproxy代码是一份优秀的c语言,为此,在twemproxy的代码中会大篇幅使用c指针.但是不论是普通类型的指针还是函数指针,都可以让我们这 ...
- <meta>标签的作用
<META> 是放于 <HEAD> 与 </HEAD>之间的标记,功用与变化等对,所以我公式化地介绍. <meta name="Descriptio ...
- .NET基础——数组
这一篇,我们来看C#中的数组. 1. 数组的概念 数组:存储相同类型多个数据元素的容器 数组的声明和初始化: 在创建数组的时候,必须指定数组的长度 ]; ,, }; ] { , , };//数组元素的 ...
- 【Scala】Scala之String
一.前言 大数据领域的Spark.Kafka.Summingbird等都是由Scala语言编写而成,相比Java而言,Scala更精炼.由于笔者从事大数据相关的工作,所以有必要好好学习Scala语言, ...
- 关于一条定制长按Power键弹出Dialog的需求
如题,需要定制长按Power键弹出的Dialog,UI上的大致效果是:全屏,中间下拉按钮“Swipe Down To Power Off”下拉关机,底部左右两侧“Reboot”,“Cancel”按钮, ...
- YARN的capacity调度器主要配置分析
yarn中一个基本的调度单元是队列. yarn的内置调度器: 1.FIFO先进先出,一个的简单调度器,适合低负载集群.2.Capacity调度器,给不同队列(即用户或用户组)分配一个预期最小容量,在每 ...
- [SQL基础教程] 5-1视图
[SQL基础教程] 5-1视图 视图和表 从SQL角度看视图就是一张表 视图与表的差别 表保存了实际的数据,视图保存的是SELECT语句: 视图的优点 节省存储空间: 将常用的Select 语句保存成 ...