15-Swift闭包

2017-04-10  本文已影响30人  诠释残缺

闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。

1.闭包表达式

1> sorted方法

Swift 标准库提供了名为 sorted(by:) 的方法,它会根据你所提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,sorted(by:)方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 sorted(by:) 方法修改。

let name = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backward(_ s1: String, _ s2: String) -> Bool {
        return s1 < s2
}
var reversedNames = names.sorted(by: backward)
print(reversedNames)
// 输出结果  ["Alex", "Barry", "Chris", "Daniella", "Ewa"]

2> 闭包表达式语法

闭包表达式参数 可以是 in-out 参数,但不能设定默认值。也可以使用具名的可变参数(译者注:但是如果可变 参数不放在参数列表的最后一位的话,调用闭包的时时编译器将报错。可参考这里)。元组也可以作为参数和返 回值。

 { (parameters) -> returnType in 

    statement
 }

// 闭包表达式版本
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})
print(reversedNames)
//由于这个闭包的函数体部分如此短,以至于可以将其改写成一行代码:
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 < s2 })
print(reversedNames)

3> 根据上下文推断类型

因为排序闭包函数是作为 sorted(by:) 方法的参数传入的,Swift 可以推断其参数和返回值的类型。 sorted(by:) 方法被一个字符串数组调用,因此其参数必须是 (String, String) -> Bool 类型的函数。这意味着 (String, String) 和 Bool 类型并不需要作为闭包表达式定义的一部分。

reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
print(reversedNames)

4> 单表达式闭包隐式返回

单行表达式闭包可以通过省略 return 关键字来隐式返回单行表达式的结果,sorted(by:) 方法的参数类型明确了闭包必须返回一个 Bool 类型值。因为闭包函数体只包含 了一个单一表达式( s1 > s2 ),该表达式返回 Bool 类型值,因此这里没有歧义, return 关键字可以省略。

reversedNames = names.sorted(by: { s1, s2 in s1 < s2 })
print(reversedNames)

5> 参数名称缩写

Swift 自动为内联闭包提供了参数名称缩写功能,你可以直接通过 $0 , $1 , $2 来顺序调用闭包的参数,以此类推。
如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会 通过函数类型进行推断。

reversedNames = names.sorted(by: { $0 > $1 } )
print(reversedNames)

6> 运算符方法

Swift 的 String 类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个 String 类型的参数并返回 Bool 类型的值。而这正好与sorted(by:) 方法的参数需要的函数类型相符合。
你可以简单地传递一个大于号,Swift 可以自动推断出 你想使用大于号的字符串函数实现

reversedNames = names.sorted(by: >)
print(reversedNames)

2.尾随闭包

如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // 函数体部分
}
// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure(closure: {
    // 闭包主体部分
})
// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
    // 闭包主体部分
}

通过传递一个尾随闭包给 numbers 数组的 map(_:) 方法来创建对应的字符串版本数组:

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
}
print(strings)  //  输出结果 ["OneSix", "FiveEight", "FiveOneZero"]

3.值获取

闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}
//makeIncrementer返回类型为() -> Int。这意味着其返回的是一个函数,而非一个简单类型的值。该函数在每次调用时不接受参数,只返回一个Int类型的值。

let incrementByTen = makeIncrementer(forIncrement: 10)
print(incrementByTen())  //  10
print(incrementByTen())  //  20
print(incrementByTen())  //  30
let incrementBySever = makeIncrementer(forIncrement: 7)
print(incrementBySever())  //  7
print(incrementByTen())  //  40
print(incrementBySever())  //  14

4.闭包是引用类型

incrementBySeven 和 incrementByTen 都是常量,但是这些常量指向的闭包仍然可以增加其捕 获的变量的值。这是因为函数和闭包都是引用类型。

let alsoIncrementByTen = incrementByTen
print(alsoIncrementByTen())  //  50

5.逃逸闭包

当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。
当定义接受闭包作为参数的函数时,可以在参数名之前标注 @escaping ,用来指明这个闭包是允许“逃逸”出 这个函数的。


var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

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)  //  200

completionHandlers.first?()
print(instance.x)  //  100

6.自动闭包

自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine)
// 打印 ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

let customerProvider = { customersInLine.remove(at: 0)}
print(customersInLine.count)  //  5

print("Now serving \(customerProvider())!")
// 打印 Now serving Chris!
print(customersInLine.count)  //  4


func serve(customer customerProvider: () -> String){
    print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0)})
//  打印 Now serving Alex!

print(customersInLine)
// 打印 ["Ewa", "Barry", "Daniella"]

func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// 打印 Now serving Ewa!

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.") 
// 打印 "Collected 2 closures."
for customerProvider in customerProviders {
    print("Now serving \(customerProvider())!")
}
// 打印 "Now serving Barry!"
// 打印 "Now serving Daniella!"

闭包部分建议 多看文档 多练

1.最简单的闭包
let demo = {
    print("Hello Swift !")
}
demo()
// 打印 Hello Swift !
2.带参数的闭包

1> 参数,返回值,实现代码都是写在 { } 中
2> 必须以 "in" 关键字分隔定义和实现
3> 格式: 形参列表 -> 返回值类型 in 实现代码

// 无返回值,有参数
let demo1 = { (x: Int) -> () in
    print(x)
}
demo1(100)
// 打印 100

// 有返回值,有参数
let demo2 = {
    (x: Int) -> (Int) in
    return 100 + x
}
print(demo2(100))
// 打印 200
上一篇 下一篇

猜你喜欢

热点阅读