Swift中闭包详解
github地址: https://github.com/corderguo
本文地址: http://coderperson.com/2016/03/10/iOS-closure/
欢迎转载,请注明出处,谢谢。
闭包的概念
Swift 中闭包的概念就像是Objective-C中的block。OC中的block类似于匿名函数,闭包是用来定义函数, 同时闭包可以嵌套和作为参数传递。 在 Swift 中,函数也只不过是一种特殊的闭包
闭包与Block的对比
作用:
- Block是用来保存一段代码,在需要的时候执行
- 闭包也是用来保存一段代码,在需要的时候执行
- 一般都是用来做耗时操作;
风格:
我们首先回顾一下OC中的Block写法:1
2
3
4
5
6
7
8// 定义block
@property (nonatomic, copy) void(^myBlock)();
// 定义block
self.myBlock = ^{
// 具体的回调操作
}
// 调用block
self.myBlock();
通过回顾,我们可以想起来Block的定义格式为:
Block格式:返回值类型(^block名称)(参数列表)
有的时候我们忘记了Block的书写格式,或者嫌这样写麻烦,我们可以直接在Xcode中敲inlineBlock
然后回车,系统会直接提醒你Block的样式;
Swift中闭包的基本格式:
1 | { |
其实这些格式我们都没必要专门去记忆,我们可以直接敲系统的带有闭包或者Block的函数,然后就一目了然了;
闭包的几种格式
第一种情况:
- 将闭包通过实参传递给函数
- 如果闭包是函数的最后一个参数,那么闭包可以写到函数{}的后面
- 如果函数只接受一个参数,并且这个参数是闭包,那么()可以省略
例如下面这三种写法是一样的:
1 | // 定义函数,参数为闭包 |
第二种情况:闭包的简写
- 如果闭包没有参数也没有返回值,那么 in 之前的东西可以删除,包括 in
例如上面闭包,还可以写成下面的形式:
1 | loadData { |
其实这些格式,你在开发中只需要掌握一种即可,或者你只需要记住闭包的定义,然后敲回车,系统会为你补齐格式;
闭包的循环引用问题
关于闭包中的 self 问题
在Swift开发中,有一个原则就是能不写self就不写self,但是在闭包中必须写上self;
这是因为闭包是用来保存一段代码,而且系统也不知道这段代码具体的调用时间,所以为了保证闭包中的对象不被释放,需要 self 进行一次强引用;这其实和Block中的原理差不多。
所以以后看到self基本上都和闭包有关系。(这也是闭包中循环引用来源的原因)
下面我举一个简单的关于闭包循环引用的例子:
你定义了两个控制器:OneController和TwoController,OneController只是负责push出TwoController,我们在TwoController中进行一些关于闭包的操作,然后在pop返回的时候查看该控制器是否被销毁了,来验证闭包是否发生了循环引用问题;
在TwoController中我们只需要简单写一些代码即可:
1 | // 定义一个闭包的属性 |
当我们执行以上代码,并且从TwoController返回到OneController时,TwoController的deinit方法没有被调用,表明TwoController没有被销毁,闭包存在了循环引用的问题;
这是因为:控制器通过闭包属性引用闭包,而闭包中又强引用着self(控制器),所以导致了循环引用的问题;
OC中关于循环引用的解决方案
1 | __weak typeof(self) weakSelf = self; |
Swift中关于循环引用的解决方案
1 | weak var weakSelf = self |
那么原先代码中只需要把self改成weakSelf即可
1 | // 由于weakSelf为可选类型,这里必须保证有值,所以加上!号 |
其它
关于定义闭包属性的问题
在Swift中,如果在某个类中定义一个属性,那么这个属性必须要初始化,否者会报错,如果暂时不想初始化,那么可以在后面写上一个 ? 号
但是在定义闭包的属性时,一定要注意,以下这种写法是最常见的一种错误写法:
1 | /** 当前写法代表闭包的返回值可以是nil,而不是初始化的闭包 */ |
我们都是热爱移动开发的程序猿,加入我们,共同提高: