实例方法在Swift中是一种把实例作为参数并且返回一个函数,并将这个函数应用于实例的方法。
柯里化
柯里化的理念是任何多参数的函数能被转化成一个只含有一个参数的脸型结构的函数。
例如,假设你有个(Int, Double) -> String
类型的函数--输入一个Int
型和一个Double
型,然后返回一个字符串。如果我们柯里化这个函数,我们能得到(Int) -> (Double) -> (String)
,即一个函数输入一个Int
型的参数然后返回第二个函数;第二个函数输入一个Double
型参数,然后返回最终的字符串。这就叫做柯里化函数,将两个方法链型调用:
let result: String = f(42)(3.14)
// f takes an Int and returns a function that takes a Double.
我们为什么需要这么做?柯里化函数最大的优势就是他能被部分应用,即在函数在最终调用之前可以指定一些参数。这就意味着,可以把函数作为参数来进行传递。
Swift对于实例方法使用同样的策略。虽然准确的说,Swift中的实例方法并不是完全的柯里化,但是还是这么叫下来的。
例子
看下面这个BankAccount
类:
class BankAccount {
var balance: Double = 0.0
func deposit(_ amount: Double) {
balance += amount
}
}
我们可以创建一个这个类的实例方法然后调用deposit()
方法:
let account = BankAccount()
account.deposit(100) // balance is now 100
同样的,我们也可以这样调用:
let depositor = BankAccount.deposit(_:)
depositor(account)(100) // balance is now 200
以上两种调用方式是完全相等的。我们首先将方法赋值给一个变量。注意:不需要在BankAccount.deposit(_:)
后传递参数,我们不是在调用它(同时调用也会出现错误,因为对于一个类不能调用实例方法),而仅仅只是引用它,很像在C语言中的方法指针。第二步是调用存在depositor
变量中的方法。类型如下:
let depositor: BankAccount -> (Double) -> ()
换句话来说,这个函数只有一个参数,一个BankAccount
实例,并且返回了另一个函数。后边这个函数用Double
型作为参数然后返回。你应该识别在deposit()
实例方法在第二部分的标记。
我希望,你能了解到Swift中的实例方法其实是一个类型方法:把一个实例作为参数然后返回一个能被用于实例的方法。当然,我们也能用一条线性来实现这个,这样就使得类型方法和实例方法之间的关系非常清晰:
BankAccount.deposit(account)(100) // balance is now 300
通过BankAccount.deposit()
的例子,实例与函数之间是绑定的。在第二步,使用其他参数调用函数。
Swift中的Target-Action
的使用
这种模式相比于闭包作为回调是更好的,尤其是当接收对象在大量不确定的时间里持有闭包。因为使用闭包会强制要求API的调用者去避免循环引用。使用target-action
模式,提供API的对象能在内部进行强弱切换,这显然会使调用端的代码变得更加整洁。
例如,一个使用target-action
模式的Control
类在Swift中是下面这样:
protocol TargetAction {
func performAction()
}
struct TargetActionWrapper<T: AnyObject> : TargetAction {
weak var target: T?
let action: (T) -> () -> ()
func performAction() -> () {
if let t = target {
action(t)()
}
}
}
enum ControlEvent {
case touchUpInside
case valueChanged
// ...
}
class Control {
var actions = [ControlEvent: TargetAction]()
func setTarget<T: AnyObject>(_ target: T, action: @escaping (T) -> () -> (), controlEvent: ControlEvent) {
actions[controlEvent] = TargetActionWrapper(target: target, action: action)
}
func removeTargetForControlEvent(controlEvent: ControlEvent) {
actions[controlEvent] = nil
}
func performActionForControlEvent(controlEvent: ControlEvent) {
actions[controlEvent]?.performAction()
}
}
使用时:
class MyViewController {
let button = Control()
func viewDidLoad() {
button.setTarget(self, action: MyViewController.onButtonTap, controlEvent: .touchUpInside)
}
func onButtonTap() {
print("Button was tapped")
}
}