从OC—Block到Swift—Closure
写在前面
阅读别人的代码,特别是一些被广泛认可的开源项目的源码,是快速提升自己能力的重要途径。由于个人编程风格的问题,有的大神写的代码就比较晦涩难懂,而且跳跃性很强,经常以跟不上ta的思路而宣告GG。对于我自己来说,我是比较不喜欢阅读Block/Closure特别多的代码的,用着很舒心,读着很糟心。。。
应该有很多(?)和我一样被无数Block或者闭包导致阅读过程中出现不安感的同学,在重新查阅了一些之前的笔记和大神们的资料后,斗胆在这里做一个总结,希望能为读到这里的同学们提供些许的念头通达。
篇幅略长,尽量不赘述,前面是block,中间是closure,最后一些tips,请选择性观看。
利益相关
本文原创,其中有引用别人帖子/博客/回答/etc, 参考内容较多不一一列举,如果您发现有侵权行为或者在意没有注明引用,请
@龙傲天
并联系我,等我看到邮件再说我会在第一时间更正。
时刻保持您的谨慎与怀疑,因为我说的并不是真理。
本文不保留任何权益,开心就好。
概述
Block(代码块)是苹果在iOS4开始引入的对C语言
的扩展,是一种特殊的数据类型。它本身封装了一段代码并将这段代码当做变量,通过block()的方式进行回调。
Closure(闭包)是Swift在吸收众家之长的基础上的特性之一,是自包含的功能代码块,可以在代码中使用或者用来作为参数传值。
这里做个简单介绍:
Block的功能:
- 定义变量
- 作为参数
- 作为返回值
- 保存一段代码,在需要的时候调用
Closure的功能:
Swift中的闭包使用情况远比Objective-C中的多,以至于闭包就是Swift的基本特性之一,和String/Array是一样的使用频率。
Closure的功能大于等于Block的功能,并有更多的优化。
::注:::Closure != Block,即闭包不是Block的Swift版。
block的声明和赋值
相信很多人(也许只有我)很难在不参考任何资料或者在编辑器的支持下正确的定义一个block,这时推荐Xcode自带的代码块来声明它:
// block声明
返回值类型(^block名称)(参数列表)
// block赋值
Block变量 = ^(参数列表){函数体};
// block声明同时赋值
返回值类型(^block名称)(参数类型) = ^(参数列表) {
内部的实现方法 / 参数处理 / 想要在别处调用而保存的代码
};
光说不练假把式,现在我们来写一个block让它来”HelloWorld”:
// 可以新建一个工程,然后在 viewDidLoad 中进行简单测试,开始吧
// 声明一个无返回值,参数为两个字符串对象,叫做myBlock的Block
void(^myBlock)(NSString *a, NSString *b);
// 形参变量名称可以省略,只留有变量类型即可(这个不是必要操作,但是要知道可以这样写)
void(^myBlock)(NSString *, NSString *);
// 给myBlock赋值
myBlock = ^(NSString *a, NSString *b) {
NSLog(@"%@ %@",a,b);
};
// 调用myBlock
myBlock(@"hello",@"world");
// 控制台输出结果
"hello world"
更为复杂的block也无非就是在这个基础上改变参数列表,增加函数体内的方法。Is it easy?
注: block的声明与赋值只是保存了一段代码段,必须调用才能执行内部代码
对于block的底层原理在这里不做探究, 因为其实我也没研究懂 因为本文重点是面向实际使用过程的,有兴趣的同学可以自行查询。
传送门: block源码下载
唐巧—谈Objective-C block的实现
block特性
- block被Objective-C看成是对象来处理
- block对于外部变量默认是只读属性
- block的代码是内联的,效率高于函数调用
- block的声明与赋值只是保存了一段代码段,必须调用才能执行内部代码
使用typedef定义Block类型
在实际使用Block的过程中,我们可能需要重复地声明多个相同返回值,相同参数列表
的Block变量,如果总是重复地编写一长串代码来声明变量会非常繁琐,所以我们可以使用typedef
来定义Block类型
(还记得之前我们的myBlock吗,现在把它定义成一个类型):
// 将下面这个接受两个NSString参数,返回为空的block类型定义为名字为MyBlock的类型
typedef void(^MyBlock)(NSString *, NSString *);
// 使用的时候指定要使用MyBlock类型的,新建一个该类型的block对象,并定义它的工作内容
MyBlock myNewBlock = ^(NSString *a, NSString *b) {
NSLog(@"%@ %@",a,b);
};
// 让block开始工作
myNewBlock(@"hello",@"myNewBlock");
// 输出结果
"hello myNewBlock"
// 再次创建一个MyBlock类型的对象,并给它定义与之前不同的工作内容
MyBlock anotherBlock = ^(NSString *str1, NSString *str2) {
NSLog(@"only print str2: %@",str2);
};
// 让block开始工作
anotherBlock(@"hello",@"world");
// 输出结果
"only print str2: world"
Block作为函数参数
之前的情形是直接调用block,让它执行自己内部封装的方法,更多的时候是将block当做参数传递并进行操作的,继续我们的例子: