An instance method in Swift is just a type method that takes the instance as an argument and returns a function which will then be applied to the instance.

recently learned about a Swift feature that blew my mind a little. Instance methods are just curried functions that take the instance as the first argument. What’s a curried function, you ask?

The basic idea behind currying is that a function can be partially applied, meaning that some of its parameter values can be specified (bound) before the function is called. Partial function application yields a new function.

Example

Consider this simple example of a class that represents a bank account:

1
2
3
4
5
6
7
class BankAccount {
var balance: Double = 0.0 func deposit(amount: Double) {
balance += amount
}
}

We can obviously create an instance of this class and call the deposit() method on that instance:

1
2
let account = BankAccount()
account.deposit(100) // balance is now 100

So far, so simple. But we can also do this:

1
2
let depositor = BankAccount.deposit
depositor(account)(100) // balance is now 200

This is totally equivalent to the above. What’s going on here? We first assign the method to a variable. Pay attention to the lack of parentheses after BankAccount.deposit — we are not calling the method here (which would yield an error because you can’t call an instance method on the type), just referencing it, much like a function pointer in C. The second step is then to call the function stored in the depositor variable. Its type is as follows:

1
let depositor: BankAccount -> (Double) -> ()

In other words, this function has a single argument, a BankAccount instance, and returns another function. This latter function takes a Double and returns nothing. You should recognize the signature of the deposit() instance method in this second part.

I hope you can see that an instance method in Swift is simply a type method that takes the instance as an argument and returns a function which will then be applied to the instance. Of course, we can also do this in one line, which makes the relationship between type methods and instance methods even clearer:

1
BankAccount.deposit(account)(100) // balance is now 300

By passing the instance to BankAccount.deposit(), the instance gets bound to the function. In a second step, that function is then called with the other arguments. Pretty cool, eh?

Implementing Target-Action in Swift

Christoffer Lernö shows in a post on the developer forums how this characteristic of Swift’s type system can be used to implement the target-action pattern in pure Swift. In contrast to the common implementation in Cocoa, Christoffer’s solution does not rely on Objective-C’s dynamic message dispatch mechanism. And it comes with full type safety because it does not rely on selectors.

This pattern is often better than using plain closures for callbacks, especially when the receiving objects has to hold on to the closure for an indeterminate amount of time. Using closures often forces the caller of the API to do extra work to prevent strong reference cycles. With the target-action pattern, the object that provides the API can do the strong-weak dance internally, which leads to cleaner code on the calling side.

For example, a Control class using target-action might look like this in Swift (adopted from a dev forums post by Jens Jakob Jensen):

Update July 29, 2014: Made the action property in TargetActionWrapper non-optional. target must be optional because it is weak.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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: (T) -> () -> (), controlEvent: ControlEvent) {
actions[controlEvent] = TargetActionWrapper(target: target, action: action)
} func removeTargetForControlEvent(controlEvent: ControlEvent) {
actions[controlEvent] = nil
} func performActionForControlEvent(controlEvent: ControlEvent) {
actions[controlEvent]?.performAction()
}
}

Usage:

1
2
3
4
5
6
7
8
9
10
11
class MyViewController {
let button = Control() func viewDidLoad() {
button.setTarget(self, action: MyViewController.onButtonTap, controlEvent: .TouchUpInside)
} func onButtonTap() {
println("Button was tapped")
}
}

Instance Methods are Curried Functions in Swift的更多相关文章

  1. Swift编程语言学习12 ——实例方法(Instance Methods)和类型方法(Type Methods)

    方法是与某些特定类型相关联的函数.类.结构体.枚举都能够定义实例方法:实例方法为给定类型的实例封装了详细的任务与功能.类.结构体.枚举也能够定义类型方法:类型方法与类型本身相关联.类型方法与 Obje ...

  2. Mongoose 'static' methods vs. 'instance' methods

    statics are the methods defined on the Model. methods are defined on the document (instance). We may ...

  3. UIView 实例方法 Instance Methods(转)

    好了,我接着上篇,开始我们的对UIView 实例方法的探索 UIView 实例方法 Instance Methods 初始化一个视图 - (id)initWithFrame:(CGRect)aRect ...

  4. [Compose] 14. Build curried functions

    We see what it means to curry a function, then walk through several examples of curried functions an ...

  5. Java SE 8 docs——Static Methods、Instance Methods、Abstract Methods、Concrete Methods和field

    一.Static Methods.Instance Methods.Abstract Methods.Concrete Methods ——Static Methods:静态方法 ——Instance ...

  6. [Ramda] Convert Object Methods into Composable Functions with Ramda

    In this lesson, we'll look at how we can use Ramda's invoker and constructNfunctions to take methods ...

  7. Static and Instance Methods in JavaScript

    class.method/instance method https://abdulapopoola.com/2013/03/30/static-and-instance-methods-in-jav ...

  8. Android JNI 学习(八):Calling Instance Methods Api

    一.GetMethodID jmethodIDGetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig); 返回 ...

  9. Swift Standard Library: Documented and undocumented built-in functions in the Swift standard library – the complete list with all 74 functions

    Swift has 74 built-in functions but only seven of them are documented in the Swift book (“The Swift ...

随机推荐

  1. 2014-10-23 NOIP模拟赛

    NOIP2014模拟赛 -----lwher 时限均为1s,内存 256MB 1.Jams倒酒(pour) Jams是一家酒吧的老板,他的酒吧提供2种体积的啤酒,a ml 和 b ml,分别使用容积为 ...

  2. 【IDEA】关于 IDEA 中新建 web 项目的 webapp 文件夹没有小蓝点 ,启动服务,访问不到解决方案

    问题描述: 新建的 maven 的 Module 项目,webapp 文件夹也是在创建完项目后手动添加的,出现了 webapp 文件夹不能被识别的情况. 解决方案: 第一步:  选中项目按 F4 键, ...

  3. bzoj5148:[BeiJing2018]Kakuro

    传送门 有上下界最小费用可行流,行列建边变形. 行列建边相信大家都做过,没做过的可以看一下这个题:bzoj3698XWW的难题,对应的我写的题解题解 这个题需要变形一下,不只是单纯的对行列进行连边,首 ...

  4. python 基础(十二) 图片简单处理

    pillow 图片处理模块 安装 pip install pillow  pip是安装第三方模块的工具 缩放图片实例 from PIL import Image path = r'C:\Users\x ...

  5. [洛谷P2186] 小Z的栈函数

    题目链接: 传送门 题目分析: 大模拟,先得存操作,然后再处理每个数-- 有一个小优化,在处理操作的时候顺便判一下最后栈里是不是有且仅有一个数,但A完了才想起来,所以就算了-- 总之就是个模拟题--没 ...

  6. SpirngMVC-JSON

    Springmvc默认用MappingJacksonHttpMessageConverter对json数据进行转换,需要加入jackson的包,如下: 配置json转换器 在注解适配器中加入messa ...

  7. [未读]深入浅出node.js

    还没看过,据说很多内容来自国外译文.

  8. 牛客网Java刷题知识点之Iterator和ListIterator的区别

    不多说,直接上干货! https://www.nowcoder.com/ta/review-java/review?query=&asc=true&order=&page=21 ...

  9. Sql Server 排序规则字符集的冲突问题

    可通过如下sql 进行修改: 如果整个DB都不一致: Alter database Expense_Portal collate Chinese_PRC_CI_AS 某张Table的栏位不一致: ) ...

  10. jsp第一章 动态网页开发基础

    动态网站可以实现交互功能,如用户注册.信息发布.产品展示.订单管理等等: 动态网页并不是独立存在于服务器的网页文件,而是浏览器发出请求时才反馈网页: 动态网页中包含有服务器端脚本,所以页面文件名常以a ...