Closures In Swift4.2
what is closures ?
· closure expressions
· shorthand way
· trailing closures
· capturing values
· closures are reference types
· escaping closures ( @escaping )
· @autoclosure
- what are closures
Note
“Closures are self-contained blocks of functionality that can be passed around and used in your code”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 4.2).” iBooks.
self-contained :
(of a thing) complete , or having all that is needed, in itself (Excerpt from Dictionary)
意味着闭包拥有所有作为一个功能块应具备的元素。而闭包本身可以理解为函数,也就是说闭包具有函数所有构成的必要要素:parameters , value types , return types ,statement
- closures 三种形式
- 有函数名的全局函数,不能捕捉任何值
2.有函数名的嵌套函数,可以捕捉外层函数里的值
3.闭包表达式Closure expressions
swift中闭包类似于其它语言中的block和Python中的lambda,这里简略引用Python的一些代码
-
lambda in python
add_function = lambda x1,x2 : x1 + x2
#等号右边代码为一个具有参数x1,x2的无函数名函数,其返回值为x1 + x2的结果
add_function(1 ,1)
#右边的函数赋给了变量add_function, type()函数返回的add_function的类型为 函数
>>> 2
type( add_function)
>>> <class 'function'>
-
swift中的闭包
Eg 1.1
let add_function = {(x1,x2) -> Int in return x1 + x2}
// { }中的表达式即为闭包,没有函数名的函数,其参数为x1,x2,返回结果为x1 + x2的结果
add_function(1, 1)
/* 由下面摘自swift programming language的文段可知闭包具有参数和返回值类型推断功能
上面 ( ) 中的参数并未指定参数类型,swift从传入的参数中推断,并且自动推断参数为
(_ x1 , _ x2) 没有标签(argument label)的参数。
*/
标签及函数参数等参考 closure expression这一部分下方的 [ function]部分
closure expression
Inside swift
闭包表达式一般形式为 :
{ ( parameters ) -> return type in statement }
参数可省略标签和类型,例: (s1,s2)
Eg 1.1中即为闭包表达式一般形式
- 闭包作为定义函数的参数:
Inside python
1.2 def functionWithLambda(s,func): return func(s) ValuesToBePrinted = ["one","two","three","four"] output = funcWithLambda(ValuesToBePrinted[0],lambda s_ : print(s_))
这里定义了一个函数,具有参数s , func 其中func为lambda函数,而s既是functionWithLambda()的参数,亦传递给传入的lambda函数作为其唯一参数。这里的lambda函数没有返回值
Inside swift
1.3 var valuesToBePrinted = ["one","two","three","four"] func DeletFirstItem(_ closureFunction:()->String){ print("The first item is \(closureFunction())") } DeletFirstItem( { valuesToBePrinted.remove(at: 0) }) }
[ function]
这里简短介绍swift中函数参数和返回值的几种形式
- func function (parm : String)->Any{ } 单参单返回
- func function ()->Any{ } 无参单反回
- func function (parm1 : String , parm2: String)->Any{ } 多参单反回
- func function (parm : String){ } 单参无返回
- func function (parm1 : Int , parm2: Int) -> ( First : Int , Second: Int) { }
多参多返回- func function ( parm : [Int] ) { } 数组类型参数
- func function ( parm : Set<Int>) { } 集合类型参数
- func function ( parm : [Int : Int] ) { } 字典类型参数
- func function (_ parm : (Int , Int) -> Int) 函数类型作参数
( )内为参数函数的参数类型,这里表示作参数的函数有两个整型参数,一个整型返回值- argument label , 在函数参数名前,调用函数时传参用参数标签代替参数名
例 : func somefunction( parameter : Int ) //最一般形式,无参数标签 somefunction( parameter : 2) // parameter 为形参名 func Anotherfunction( A parameter : Int) //带有标签 “ A ”的参数 Anotherfunction( A : 2) //带有标签则调用函数时用标签代替参数名 func AndAnotherfunction( _ parameter : Int) // 标签用 " _ " 代替 AndAnotherfunction( 2 ) // 调用时则直接传入参数值 任何一个参数方式都可以,一般采用带标签参数形式
函数作返回类型 ,function as return types
Code:func OuterFunction(_ x1: Int)->()-> Void{ var x = x1 func insideNestedFunction()-> Void{ print("\(x)")} return insideNestedFunction } let function = OuterFunction(6) function()
嵌套函数 insideNestedFunction 是无参数无返回函数
( ) -> Void
因此外层OuterFunction函数 的返回类型为 ( )-> Void
: func OuterFunction(_ x: Int) -> () -> Void()
-
Autoclosure
“ A autoclosure is a closure that is automatically created to wrap an expression that's being passed as an argument to a function"
简而言之就是自动闭包就是能够自动将作为函数参数的闭包表达式用括号括起来,使用自动闭包可以少写一个” { } “
将上面1.3例重写如下
1.3.1
var valuesToBePrinted = ["one","two","three","four"]
func DeletFirstItem(_ closureFunction : @autoclosure () -> String){
print("The first item is \(closureFunction())") // [1]
}
DeletFirstItem( valuesToBePrinted.remove(at: 0) ) // [2]
}
上面1.3.1 即为1.3 的自动闭包版。改动就[1],[2]两处,其中[1]处在参数类型前加上了自动闭包标记 @autoclosure 。 在[2]处去掉了闭包表达式的花括号 { } 。 官方文档不建议大量使用@autoclosure , 大量使用会使代码难以阅读
-
shorthand
“
· Inferring parameter and return value types from context
· Implicit returns from single-expression closures
· Shorthand argument names
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 4.2).” iBooks.
这里例子都为闭包作为调用函数时的参数
1.4
let lists = ["B","C","A","E","D"]
1.4.1 var reverseLists = lists.sorted(by: { (s1 : String ,s2 : String)->
Bool in return s1 > s2 }) //一般闭包作参数形式
1.4.2 reverseLists = lists.sorted(by: { s1 , s2 in return s1 > s2 } )
// 自动推断参数类型,这里根据lists中参数自动推断 s1,s2 为String类型,因此可省略()
1.4.3 reverseLists = lists.sorted(by: { s1 , s2 in s1 > s2})
// 由单个表达式 s1 > s2 自动推断出返回类型为Bool值,这里没有歧义,所以可省略 return
1.4.4 reverseLists = lists.sorted(by: {$0 > $1})
/*在闭包表达式中使用shorthand argument names 则可省略参数名,类型,返回类型
$0表示第一个参数,以此类推 */
1.4.5 reverseLists = lists.sorted(by: > )
/* swift的 String类型定义了比较操作 > 用于比较 String类型的两个参数,并返回Bool值,
这正好与 sorted(by:) 函数相匹配,因此可省略其余参数,只写操作方法 ' > '
-
Trailing Closure
尾随闭包其实也是个很简单的东西,其目的就是为了使代码更易于阅读。当一个函数最后一个参数为闭包表达式时,如果闭包表达式较长,那么阅读起来就不那么简明。因此这里可以用到尾随闭包,既达到了代码的作用无改变,又使代码结构更清晰。
func someFunctionThatTakesAClosure(closureFunction : () -> Void) {
// function body goes here
}
下面为不用尾随闭包的函数调用方式
someFunctionThatTakesAClosure (closureFunction: { 闭包体 } )
下面为用尾随闭包的函数调用方式
someFunctionThatTakesAClosure () { 函数体 }
省略了括号内的闭包名,移至()外
几个闭包示例
1.5.1 reverseLists = names.sorted () { $0 > $1}
1.5.2 reverseLists = names.sorted { $0 > $1}
1.5.3
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
let strings = numbers.map { (number) -> String in
var number = number
var output = ""
repeat {
output = digitNames[number % 10]! + output
number /= 10
} while number > 0
return output
}
1.5.2中 ,闭包为唯一参数,因此可省略 ' ( ) '
1.5.3中 , map()函数对数组numbers里每个元素都调用一次闭包 ;同上一条,map函数中的闭包亦是唯一参数,因此省略了 ' ( ) '
-
Capturing Values
文章开头提到 嵌套函数可以捕捉其外层函数的常量和变量。
func OuterFunction(_ x1: Int)->()-> Void{
var x = x1
func insideNestedFunction()-> Void{ print("\(x)")}
return insideNestedFunction
}
let function = OuterFunction(6)
function()
这里内层函数 insideNestedFunction 捕捉了外层函数OuterFunction的变量 ' x ' ,闭包不仅可以捕捉其周围的常量,变量,还可以修改变量的值即使原常变量都不存在了。
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
let incrementBySeven = makeIncrementer(forIncrement: 7)
两次调用 makeIncrement 函数返回的两个函数各自捕捉到的变量和常量都互不影响,相互独立。
-
escaping
Escaping Closures
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.
One way that a closure can escape is by being stored in a variable that is defined outside the function
从官方文档中可以得知,逃逸闭包就是函数中的闭包在函数返回后才会被调用。
var escapingClosure : [ () -> Void] = [ ]
1.6.1 func someFunctionWithNoneEscaping( closure: ()-> Void){
closure()
}
1.6.2 func someFunctionWithEscaping(closure : @escaping () -> Void{
escapingClosure.append ( closure)
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"
completionHandlers.first?()
print(instance.x)
// Prints "100"
引用swift programming language 中closre部分最后一段关于自动逃逸闭包的文段
If you want an autoclosure that is allowed to escape, use both the
@autoclosure
and@escaping
attributes. The@escaping
attribute is described above in Escaping Closures.
// customersInLine is ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: >@autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))
print("Collected (customerProviders.count) closures.")
// Prints "Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving (customerProvider())!")
}
// Prints "Now serving Barry!"
// Prints "Now serving Daniella!"
In the code above, instead of calling the closure passed to it as its
customerProvider
argument, thecollectCustomerProviders(_:)
function appends the closure to thecustomerProviders
array. The array is declared outside the scope of the function, which means the closures in the array can be executed after the function returns. As a result, the value of thecustomerProvider
argument must be allowed to escape the function’s scope.
计算机