委托与回调函数
class ScrollViewCtrl: UIViewController, UIScrollViewDelegate {
//some code
override func viewDidLoad() {
super.viewDidLoad()
let scrollView = UIScrollView()
scrollView.delegate = self
}
func scrollViewDidScroll(scrollView: UIScrollView) {
//do something
}
//some code
}
这一段Swift代码稀疏平常,平常到可能有的同学从未想过这段代码背后做了什么。这里其实使用了一个同样稀疏平常却广为流传的设计模式——delegate模式。Delegate模式是在Cocoa开发中随处可见的一种设计模式,几乎贯穿于整个Cocoa框架。尤其是在我们最常使用的UIKit中,控制各类UI组件的最常用手段就是delegate跟Target-action(目标-动作,类似于一个监听事件)。至于回调函数么,其实跟委托没什么关系,然而曾经年少无知的我对这两个概念十分模糊,我想可能现在也有很多同学感到一头雾水,所以今天我想针对委托模式和回调函数分别讲一讲自己的理解,如有疏漏,欢迎各位批评指正。
我们先回到开头的代码,看看到底发生了什么。ScrollViewCtrl
是一个控制器,它confirm to(遵守,大致就是其他语言中实现一个接口的意思)UIScrollViewDelegate
这个protocol
(协议)。虽然Swift的委托实现跟协议息息相关,但我在这里不准备过多讨论协议的细节内容,只要知道它类似于其他语言的interface
(接口)就可以了,只是Swift中的protocol
不仅可以被class
遵守, 还可以被struct
、enum
遵守。
接着往下说,一开始我初始化了一个UIScrollView
,并把它的delegate
属性设置成self
(即ScrollViewCtrl
的一个实例)。然后在ScrollViewCtrl
中写了一个scrollViewDidScroll
方法。这样在这个scrollView
滑动的时候就会执行scrollViewDidScroll
里面的代码了。
讲到这里我想大部分人还是不明白委托到底是怎么回事,因为我在这里只是使用了委托,具体的委托实现是UIKit框架写好的,这样对于理解委托来说无异于隔靴搔痒,所以下面我来实现一个完整的委托。
//创建
protocol MyDelegate: class {
func delegateMethod()
}
class MyClass {
weak var delegate: MyDelegate!
func doSomethingWithAdditionalInfo() {
//...
delegate.delegateMethod()
}
}
//使用
class MyCtrl: UIViewController, MyDelegate {
var myInstance: MyClass!
override func viewDidLoad() {
super.viewDidLoad()
myInstance = MyClass()
myInstance.delegate = self
myInstance.doSomethingWithAdditionalInfo()
}
func delegateMethod() {
println("Run")
}
}
上面的代码是我随手写的,没跑过,理论上会输出"Run",各位可以试一试。这其实就是一个完整的委托模式了,当然在真实的情况下,一般doSomethingWithAdditionalInfo
会在某个特定的时机运行delegate.delegateMethod
这方法,譬如它可能会是个发送异步网络请求取数据的方法,然后在取到数据后调用delegate.delegateMethod
,而取到数据后要做什么操作可以延迟到使用的时候再定义,即由使用者自行定义delegateMethod的具体操作。这是一种良好的解耦,非常适合在框架中使用。
委托说完了,那什么是回调函数呢?其实回调函数做的事情跟委托很类似。不同的是,回调函数通过传递函数来实现延迟定义操作(在C这样的过程式语言中,需要通过函数指针作为参数传递来调用函数,而在支持高阶函数的函数式语言中,可以直接以函数为参数传递进行操作)。Swift对函数式编程的支持非常到位,所以可以轻松编写回调函数。依然以上面那段代码实现的功能为例,这次我们只要:
//创建
func doSomethingWithAdditionalOperation(additionalOperation: () -> ()) {
//...
additionalOperation()
}
//使用
class MyCtrl: UIViewController, MyDelegate {
override func viewDidLoad() {
super.viewDidLoad()
doSomethingWithAdditionalOperation {
println("Run")
}
}
}