6

Day6 - Swift 闭包详解 上

 2 years ago
source link: https://mp.weixin.qq.com/s/bE-Bt0VQ8aT3TtZz9EwfYg
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Day6 - Swift 闭包详解 上

Original iOS成长指北 iOS成长指北 2020-11-17
收录于话题
#Swift 100 Days

《Swift 100 Days 》是一个长期计划,欢迎读者转发、留言、点赞。让笔者能够坚持完成这 100 天的更新。

在第五天 函数 学习中,我们学会使用 func 关键字创建了一个函数。然而,Swift 中还有另一种特殊类型的函数,称为 闭包,它可以在不使用关键字func和函数名的情况下进行定义。

与函数一样,闭包可以接受参数和返回值。它还包含一组语句,这些语句在您调用它之后执行,并且可以作为函数分配给变量/常量。

闭包作为 Swift 中的一个复杂概念。所以我们花费两天时间来进行闭包的学习。

Closures 在Swift 官方文档上是这么介绍的:

Closures are self-contained blocks of functionality that can be passed around and used in your code.

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

通俗点来说,闭包就是一个可以分配给变量的代码块。然后可以在代码中传递它,例如传递给另一个函数。然后该函数调用闭包并执行其代码,就好像闭包是一个普通函数一样。尽管它们的工作方式类似于函数,但它们的编写方式略有不同。

在   Objective-C 我们可以使用如下方法来创建一个对象。

 NSString *string = ({     @"你好"; });

这是一个GNU(非标准)C语言扩展,称为 语句表达式。gcc、clang和其他一些编译器支持该语法。笔者人认为这也是一种闭包。

闭包表达式语法一般类似于以下形式:

{ (parameters) -> return type in   statements}

注意返回类型之后使用了 in 关键字。in 关键字用于分隔闭包中的语句。闭包接受参数并可以返回值。

创建一个最基本的闭包

如同函数学习一样,我们来学习创建一个无参数无返回值的最基本的闭包。

let learnSwift = {    print("Closures are like functions")}learnSwift()let learnSwift1 = { () -> Void in    print("Closures are like functions")}learnSwift1()let learnSwift2 = { () -> () in    print("Closures are like functions")}learnSwift2()

上面的三个是等价的,我们可以在 Playground 中看到上面三个闭包的类型都是 () -> ()。正如我们所知,Swift 可以进行类型推断,那么我们可以显示表明对应变量/常量的类型

let learnSwift3: () -> () = {  print("Closures are like functions")}

闭包类型我们会在尾随闭包的时候进行详细介绍。

注意:

我理解在 {} 里实现的功能其实是一个初始化的过程,所以在使用闭包时,一定要一个接收值。并不能定义一个只初始化的功能。就像我们不能在代码的一行中写下 1

如同函数一样,闭包可以接收参数。我们可以按照下面的方法去定义一个接受字符串参数的闭包。

let sayHello = { (name: String) in    print("Hello, \(name)")}sayHello("WWH")let sayHello1: (String) -> () = { name in    print("Hello, \(name)")}sayHello1("YHG")let sayHello2: (String) -> () = {    print("Hello, \($0)")}sayHello2("SY")

Swift 用 关键字 in 来分隔闭包参数和返回值或语句。这里我们需要注意以下几点

  • 如果我们使用类型推断来定义一个闭包的话, 我们必须要定义参数名和参数类型

  • 如果我们声明时确定了参数类型, 在{ parameter in } 中,我们可以不使用 (parameter name:parameter type) 进行定义

  • 如果我们声明时确定了参数类型,我们可以的{} 中省略参数名,并用用 $0 表示

  • 如果我们声明时确定了参数类型, 我们可以的{} 中省略参数名,可以用 _ 来代替

与函数不同的是,在调用闭包时,不包括它的任何参数的名称,闭包是没有参数标签的。闭包所定义的参数名称其实主要是为了内部使用的。

传入多个参数

和函数一样,我们可以为闭包传入多个参数。

let sayHelloMultiple = { (name: String, age: Int, score: Double) in    print("\(name) is \(age) year old ,score is \(score)")}let sayHelloMultiple1: (String,Int,Double) -> Void = { name,age,score in    print("\(name) is \(age) year old ,score is \(score)")}let sayHelloMultiple2: (String,Int,Double) -> () = {    print("\($0) is \($1) year old ,score is \($2)")}sayHelloMultiple("WWH",35,87.0)sayHelloMultiple1("WWH",35,87.0)sayHelloMultiple2("WWH",35,87.0)
具有可变参数的闭包
let sayHelloMore = { (name: String...) in    print("Hello, \(name)")}sayHelloMore("XM","PED")let sayHelloMore1:(String...) -> () = { (name: String...) in    print("Hello, \(name)")}sayHelloMore1("WD","HDM", "XWQ")

与之前不一样的话,哪怕设置了函数类型,在{}也要定义这个参数,不然会报类型错误。

闭包返回值

参照表达式,我们可以在 -> 和 in 关键字之间定义函数的返回值

let drivingWithReturn = { (place: String) -> String in    return "I'm going to \(place) in my car"}let drivingWithReturn1:(String) -> String = {    return "I'm going to \($0) in my car"}let drivingWithReturn2:(String) -> String = {    "I'm going to \($0) in my car"}

同函数一样,闭包的返回值可以是任意类型的。String、Int 甚至是另一个函数。

多重返回值闭包

你同样可以用元组(tuple)类型让多个值作为一个复合值从闭包中返回。

let multipleReturn = { () -> (String, Int) in    return ("HCP", 29)}let user0 = multipleReturn()print("\(user0.0),\(user0.1)")let multipleReturn1 = { () -> (name: String, age: Int) in    return ("HL", 19)}let user = multipleReturn1()print("\(user.name),\(user.age)")
闭包的隐式返回

在 Swift 闭包处理的时候,如果闭包没有返回值,在{} 里面定义的时候,你可以隐藏 -> ()。但是注意你不可以隐藏 ()

当存在单个返回值,但是在 in 关键字后面只有一个语句时,不仅可以隐藏 -> () 也可以省略 return 关键字

let drivingWithReturn3 = { (place: String) in    return "I'm going to \(place) in my car"}let drivingWithReturn4 = { (place: String) in    "I'm going to \(place) in my car"}drivingWithReturn3("SuZhou")drivingWithReturn4("SuZhou")

单个返回值的多个返回值,或者说有多个语句的时候,还是需要定义返回值类型的。

将闭包作为函数参数传递

我们可以将闭包作为函数参数在定义函数时使用。我们然后我们在函数调用时可以使用闭包。

func play(using playType: () -> Void) {    print("Let's play a game")    playType()}play(using: {    print("Fetch!")})func deliverTalk(name: String, type: () -> Void) {    print("My talk is called \(name)")    type()}deliverTalk(name: "My Awesome Talk", type: {    print("Here's a great talk!")})

如果函数的最后一个参数是闭包,Swift 允许您使用特殊的语法,称为尾随闭包语法。不是将闭包作为参数传递,而是将它直接传递到大括号内的函数之后。尾随闭包是一种特殊的将闭包作为函数参数使用。

唯一即最后

上面的两个例子我们同样可以这样子来用

play {    print("Fetch! Trailing closure")}deliverTalk(name: "My Awesome Talk") {    print("Here's a great talk! Trailing closure")}

尾随闭包作为一种常见的 Swift 语法。需要我们好好掌握。

对比上面对于最后一个参数是闭包的函数的调用示例。我们可以看到使用尾随闭包可以让代码更加简短,可读性更好。所以出于可读性的原因,建议使用。

Xcode 中调用定义了最后一个参数是尾随闭包的参数时,会默认使用尾随闭包的样式。

在尾随闭包中传递参数

尾随闭包语法作为一种特殊的闭包用法,同样可以传递参数。

func makePizza(addToppings: (Int) -> Void) {    print("The dough is ready.")    print("The base is flat.")    addToppings(3)}makePizza { (toppingCount: Int) in    let toppings = ["ham", "salami", "onions", "peppers"]    for i in 0..<toppingCount {        let topping = toppings[i]        print("I'm adding \(topping)")    }}makePizza {    let toppings = ["ham", "salami", "onions", "peppers"]    for i in 0..<$0 {        let topping = toppings[i]        print("I'm adding \(topping)")    }}

同闭包或函数中使用参数一致,千万不要修改参数类型。

在尾随闭包中返回值

尾随闭包自然同样支持返回值了。

func playSong(_ name: String, notes: () -> String) {    print("I'm going to play \(name).")    let playedNotes = notes()    print(playedNotes)}playSong("Mary Had a Little Lamb") {    return "EDCDEEEDDDEGG"}

尾随闭包支持返回值,闭包有一个疯狂的用法,如下:

let squared = { $0 * $0 }(12)

可以在评论区发表你的意见。可不可以这样使用? 如果可以的话 squared 是什么类型?


欢迎点赞、转发、评论、在看。欢迎关注公众号。由于笔者的学习能力和语言表达能力,有任何不清楚或错误的地方欢迎在留言区留言。后续也会对文章进行修改。希望能一起学习,获得成长。

由于能力问题。虽然是一个100天的学习计划,但是没办法做到100天完成更新。尽量保证2-3天完成一篇。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK