Swift Closures — Everyday Gems Part 1 of 2
Today’s Swift topic is on Closures. Heading back to my C and C++ days, I would define a closure as a function pointer. C# developers would define them as lambdas. Similarly, Java developers would also call a closure a lambda. Finally, Javascript developers would state a closure closely resembles a callback. So what are they in Swift? A closure is basically a code block (named or anonymous) used in other places of your code using a function-like syntax.(闭包就是一段代码块,这段代码块呢,可以是命令了的,也可以是匿名的,使用这段代码块的时候的语法很像使用一个函数一样.) I realize that is a mouthful, but more advanced topics are not for the faint of heart. What I will say on the topic is that if you master closures, your Swift development skills will jump leaps and bounds!
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.简而言之,swift中的闭包很像C和OC中的blocks,像其他编程语言中的lambda表达式.
In this post, I will cover creating closures and simplifying them to their most succinct form. In Part 2 (next week’s post), we will cover more advanced features of closures such as what it means to capture values.(本篇:闭包的简版使用; 下篇:闭包中捕捉变量)
Defining and Creating Closures定义和创建闭包
Closures can be defined in three ways:
- Global functions 全局闭包
- Nested Functions 嵌套函数
- Closure expressions 闭包表达式
This article will focus on Nested and Closure Expressions.
一,Nested functions函数嵌套
When we define a function inside the body of another function, we are really creating a nested function.(函数嵌套: 在一个函数内部定义另外一个函数)
Nested functions let us control access by hiding their existence from the outside world by default. However, the inner function(s) is controlled by its enclosing function, allowing access to any values passed to or contained in the body of the enclosing function. In addition, the enclosing function could return any nested function as a return value.(包含的函数可以指定一个嵌套的函数作为返回值.)
Think of the Factory pattern from the Gang of Four guys on design patterns as an example of where a nested function might apply.
Nested functions are closures that have a name and can capture values from their enclosing function.(嵌套函数可以理解为一个闭包,这个闭包呢,捕捉了包含这个闭包所在的函数中的一些变量的值.)
Let’s look at an example to make this concrete. Below, I created a function applySalesTaxForState that contains several nested functions to apply the appropriate sales for a provided state. The return value is a function that we can call to calculate the sales tax on an amount passed as a parameter. In my case, I want to calculate the sales tax on a $100 purchase in Pennsylvania.
Each of the “apply” methods that are enclosed by the applySalesTaxForState method are considered nested functions. Once I have assigned the return of our enclosing function, I call it by referencing the variable name taxForPA and passing it an amount.
在applySalesTaxForState方法中的每一个apply方法都可以 看做是一个嵌套函数.
只要指定了外层函数的返回值,就可以通过给taxForPA传递一个值来调用.
二,Closure Expressions
Closure Expressions are unnamed (anonymous) code blocks written in a concise format. (闭包表达式是一个未命名的(也就是匿名)的一段简洁的代码块.)
Closure expressions are great because you can to define an immediately useful function without giving it a name to perform a focused task that is really only useful to the receiver of the closure. When it feels like a waste to create a function that is only needed for one spot, you consider creating a closure expression instead.(什么场景下使用?)
More experienced coders may accuse me of over simplifying the definition, but I’m also attempting to breakdown a complex concept in terms that newbie developers can grasp. Feel free to leave me comments on better ways of describing closure expressions in the response section. I believe that newcomers to Swift development could benefit from and appreciate additional definitions from other developer perspectives.(仁人见智)
* Closure expressions are a way to write inline closures in a brief, focused syntax.* Closure expressions provide several syntax optimizations for writing closures in a shortened form without loss of clarity or intent.
from The Swift Programming Language (Swift 2.1)
Now that we have a definition, let’s move on to creating a closure expression. See below for the closure expression syntax:
closure expression syntax
It is important to note that the parameter declaration, return type and expression body are all defined inside of the curly braces with the expression body starting after the in keyword.
- 参数声明,包含在一个括号内部
- 返回值类型前面使用箭头指定
- in 之后是闭包代码块
- 整个闭包代码块使用大括号包含{}
Let’s put this to use and create a closure expression. Array filtering is a common need when working with lists of objects. Apple added a filter method to allow you to provide a closure with your criteria for filtering. Let’s create an array of people and a closure expression to return a subset of the array using the filter method on an array.
In our example above, we have created a closure expression that is passed to our filter method to find elements of our array that contain the string “Ryan”. Using a closure expression makes sense since we really don’t need to search for people named “Ryan” outside of filtering our people array.
01Simplifying Closure Syntax 简化语法
The code in the last section is good, but we can do better. Let’s look at a few ways to reduce the amount of code we have to write when creating a closure.
Inferring parameters and return types
One of the nice features of Swift is its ability to infer types. (swift的一个能力是:类型推断,使用类型推断可以简化闭包的语法.)
We can use this ability to simplify our closure syntax. The Swift compiler can infer types passed as parameters or returned from functions when using a closure expression.(Swift编译器通过闭包代码快可以1) 从传递的参数推断出参数类型; 2)从返回值推断出返回值类型)
Therefore, you don’t have to implicitly set a parameter’s type or define return types. (因此可以不必显式什么参数类型和返回值类型)You certainly can add them if you feel it makes the code more readable, but they really are optional.
In the code sample above, I first demonstrate writing the closure expression using parameter and return types.
The next statement shows a closure omitting the types as they are inferred by the Swift compiler.
Finally, the last statement demonstrates creating a closure inline, allowing us to omit the return keyword since it is inferred by the compiler.
Hopefully, you can begin to see how you can simplify a closure to make it more concise.
02Using Shorthand syntax 使用速记语法
Another nice feature Swfit gives us is automatic parameter naming for inline closure expressions. (swift另外第一个特性是对闭包表达式进行了自动参数命名)
What this means is that we can leave off the parameter list entirely and Swift will name parameters for us using the following pattern: $0, $1, $2, etc to represent the first, second, third argument in a parameter list.(也就是说,我们可以不必给参数命名,就 可以使用系统自动参数命名: 第一个参数名位$0,第二个参数名位$1等等.)
In fact, you can also drop the in keyword when using the shorthand syntax. Let’s revisit our example from above and show how we could apply it to our closure statement.
In our final version of our closure expression, we are able to omit the parameter and return type along with the in and return keywords. By refering to each name of the array as $0, we are using Swift’s automatic parameter naming to shorten the amount of code we have to write. That’s pretty concise syntax if you ask me!
03Trailing Closures 尾随闭包
We can also reduce the amount of syntax we have to write in cases where we are passing a closure expression as the final argument to a method.(在一个函数的最后一个参数的是一个闭包的时候,这个函数的括号也看恶意省去.)
A trailing closure is a closure expression that is written outside of (and after) the parentheses of the function call it supports.(所以尾随闭包就是将整个闭包代码块写在函数名称之后.)
The Swift Programming Language (Swift 2.1)
Trailing closures are really useful when you want to pass a long closure expression to a method as its last argument. If the closure expression is the only argument, you can also discard the () following the function’s name. Continuing with our previous example, we could rewrite our closure expression as follows:
Final Thoughts 最后的思考
Closures are used everywhere in Swift! You might need to work through a bunch of examples to feel comfortable using them. Trust me, you don’t want to skip over this feature. Fire up Xcode and play around with them soon. You can find a copy of the Playground that I used to create the samples here. My next post will extend our topic on closures and will even give you hints on helping the Swift compiler optimize your code when using them.
If you find this post helpful, recommend it for others to read. Please visit me at www.gittielabs.com and subscribe to my RSS feed so that you won’t miss a post. I’m also putting together a video course to teach Swift development and could use your input on topics that you feel would be helpful. Thanks for reading!
class ViewController: UIViewController {
let people = ["LiLei","HanMeiMei","Jhon","Tom"]
override func viewDidLoad() {
super.viewDidLoad()
closure01()
closure02()
closure03()
closure04()
closure05()
}
/**
标准闭包: { (参数列表) -> 返回值 in xx闭包代码块xx}
*/
func closure01() {
let someOne = people.filter { (name:String) -> Bool in
return name.containsString("Tom")
}
print(someOne)
}
/**
类型推断: 去掉参数类型,返回值类型
*/
func closure02() {
let someOne = people.filter ({ (name) in
return name.containsString("Tom")
})
print(someOne)
}
/**
去掉参数列表的括号和返回值的return
*/
func closure03() {
let someOne = people.filter({ name in name.containsString("Tom") })
print(someOne)
}
/**
速记语法: 自动给闭包的参数命名,省去了in
*/
func closure04() {
let someOne = people.filter ({ $0.containsString("Tom")})
print(someOne)
}
/**
Trailing Closures: 尾随闭包 去掉括号.
*/
func closure05() {
let someOne = people.filter{$0.containsString("Tom")}
print(someOne)
}
}