Swift基础-闭包

创建基础的闭包

Swift使我们可以像使用其他类型一样使用函数,例如字符串和整数。这意味着您可以创建一个函数并将其分配给一个变量,使用该变量调用该函数,甚至将该函数作为参数传递给其他函数。

以这种方式使用的函数称为闭包,尽管它们像函数一样工作,但编写方式有所不同。

让我们从一个简单的示例开始,该示例显示一条消息:

let driving = {
    print("I'm driving in my car")
}

这样可以有效地创建一个没有名称的函数,并将该函数分配给driving。现在,您可以driving()像调用常规函数一样进行调用,如下所示:

driving()

在闭包中接受参数

创建闭包时,它们没有名称或空格来写入任何参数。这并不意味着它们不能接受参数,只是它们以不同的方式接受参数:它们在大括号内列出。

要使闭包接受参数,请在开括号后面的括号内列出它们,然后写下in让Swift知道闭包的主体已经开始了

例如,我们可以创建一个接受地名字符串作为其唯一参数的闭包,如下所示:

let driving = { (place: String) in
    print("I'm going to \(place) in my car")
}

函数和闭包之间的区别之一是,在运行闭包时不使用参数标签。因此,调用driving()我们现在写成这样:

driving("London")

从闭包中返回值

闭包也可以返回值,它们的写法与参数类似:您可以在闭包内直接在in关键字之前写它们。

为了证明这一点,我们将使用driving()闭包并使其返回其值,而不是直接打印它。这是原始的:

let driving = { (place: String) in
    print("I'm going to \(place) in my car")
}

我们希望闭包返回一个字符串而不是直接打印消息,因此我们需要在in之前使用-> String,然后像普通函数一样使用return

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

现在,我们可以运行该闭包并打印其返回值:

let message = drivingWithReturn("London")
print(message)

闭包作为参数

因为闭包可以像字符串和整数一样使用,所以可以将它们传递给函数。语法起初会让您头痛,因此我们将慢慢来。

首先,这是我们的基本driving()闭包

let driving = {
    print("I'm driving in my car")
}

如果我们想将该闭包传递给函数,以便可以在该函数内部运行,则可以将参数类型指定为() -> Void。这意味着“不接受任何参数,然后返回Void” – Swift所说的“什么都不做”。

因此,我们可以编写一个travel()接受各种旅行行为的函数,并在此之前和之后打印一条消息:

func travel(action: () -> Void) {
    print("I'm getting ready to go.")
    action()
    print("I arrived!")
}

我们现在可以使用driving闭包来调用它,如下所示:

travel(action: driving)

尾随闭包语法

如果函数的最后一个参数是闭包,则Swift可让您使用称为尾随闭包语法的特殊语法。与其在闭包中作为参数传递,不如在括号内的函数之后直接传递它。

为了证明这一点,这又是我们的travel()功能。它接受action闭包,以便可以在两个print()调用之间运行:

func travel(action: () -> Void) {
    print("I'm getting ready to go.")
    action()
    print("I arrived!")
}

因为它的最后一个参数是闭包,所以我们可以使用尾随闭包语法进行调用travel(),如下所示:

travel() {
    print("I'm driving in my car")
}

实际上,由于没有任何其他参数,我们可以完全消除括号:

travel {
    print("I'm driving in my car")
}

此时会输出如下内容:

I'm getting ready to go.
I'm driving in my car
I arrived!

尾随闭包语法在Swift中非常普遍,因此值得习惯。

当闭包接受参数时,将其用作参数

在这里,可以开始读取闭包,有点像线噪声:传递给函数的闭包也可以接受其自己的参数。

我们用过的() -> Void意思是“不接受任何参数,什么也不返回”,但是您可以继续使用您的闭包()应该接受的任何参数的类型填充。

为了说明这一点,我们可以编写一个travel()函数,该函数接受一个闭包作为唯一参数,然后该闭包又接受一个字符串:

func travel(action: (String) -> Void) {
    print("I'm getting ready to go.")
    action("London")
    print("I arrived!")
}

现在,当我们使用尾随闭包语法进行调用travel()时,需要我们的闭包代码才能接受字符串:

travel { (place: String) in
    print("I'm going to \(place) in my car")
}

此时,place的值为func travel(action: (String) -> Void)action的值。输出内容如下:

I'm getting ready to go.
I'm going to London in my car
I arrived!

返回值时使用闭包作为参数

我们一直在用() -> Void“不接受任何参数,什么也不返回”的意思,但是您可以将其替换Void为任何类型的数据以强制闭包返回一个值。

为了说明这一点,我们可以编写一个travel()函数,该函数接受一个闭包作为其唯一参数,然后该闭包又接受一个字符串并返回一个字符串:

func travel(action: (String) -> String) {
    print("I'm getting ready to go.")
    let description = action("London")
    print(description)
    print("I arrived!")
}

现在,当我们使用尾随闭包语法进行调用travel()时,需要我们的闭包代码才能接受字符串并返回字符串:

travel { (place: String) -> String in
    return "I'm going to \(place) in my car"
}

速记参数名称

我们刚刚做了一个travel()功能。它接受一个参数,这是一个闭包,其本身接受一个参数并返回一个字符串。然后在两次调用print()之间运行该闭包。

这就是代码中的内容:

func travel(action: (String) -> String) {
    print("I'm getting ready to go.")
    let description = action("London")
    print(description)
    print("I arrived!")
}

我们可以这样调用travel()

travel { (place: String) -> String in
    return "I'm going to \(place) in my car"
}

但是,Swift 知道该闭包的参数必须是字符串,因此我们可以将其删除:

travel { place -> String in
    return "I'm going to \(place) in my car"
}

它也知道闭包必须返回一个字符串,因此我们可以删除它:

travel { place in
    return "I'm going to \(place) in my car"
}

由于闭包只有一行代码必须是返回值的那一行,因此Swift也允许我们删除return关键字:

travel { place in
    "I'm going to \(place) in my car"
}

Swift具有简化的语法,可让您走得更短。除了编写place in外,我们还可以让Swift为闭包的参数提供自动名称。这些名称以美元符号命名,然后从0开始计数。

travel {
    "I'm going to \($0) in my car"
}

具有多个参数的闭包

为了确保一切都清楚,我们将使用两个参数编写另一个闭包示例。

这次,我们的travel()函数将需要一个闭合符,该闭合符指定某人要去的地方以及他们要走的速度。这意味着我们需要使用(String, Int) -> String参数的类型:

func travel(action: (String, Int) -> String) {
    print("I'm getting ready to go.")
    let description = action("London", 60)
    print(description)
    print("I arrived!")
}

我们将使用尾随闭包和简写闭包参数名称来调用它。由于此方法接受两个参数,因此我们将同时获得$0$1

travel {
    "I'm going to \($0) at \($1) miles per hour."
}

有些人不喜欢使用速记参数名称,$0因为这样可能会造成混淆,这没关系–尽一切可能为您效劳。

从函数返回闭包

和你可以传递一个闭包给函数的方式相同,你也可以从函数获取返回的闭包。

第一次使用的语法有些混乱,因为它使用两次->:一次用于指定函数的返回值,第二次用于指定闭包的返回值。

为了解决这个问题,我们将编写一个不接受任何参数但返回闭包的函数travel()。返回的闭包必须使用字符串调用,并且不会返回任何内容。

这是Swift中的样子:

func travel() -> (String) -> Void {
    return {
        print("I'm going to \($0)")
    }
}

现在,我们可以调用travel()以获取该闭包,然后将其作为函数调用:

let result = travel()
result("London")

从技术上讲是允许的-尽管实际上不建议这样做!–travel()直接从以下位置调用返回值:

let result2 = travel()("London")

值捕获

如果您在闭包内部使用任何外部值,则Swift会捕获它们-将它们存储在闭包旁边,因此即使它们不再存在,也可以对其进行修改。

现在,我们有一个返回闭包的函数travel(),并且返回的闭包接受字符串作为其唯一参数,并且不返回任何内容:

func travel() -> (String) -> Void {
    return {
        print("I'm going to \($0)")
    }
}

我们可以调用travel()以获取闭包,然后自由调用该闭包:

let result = travel()
result("London")

如果我们在闭包内部使用travel()要创建的值,则会发生闭包捕获。例如,我们可能想跟踪返回的闭包被调用的频率:

func travel() -> (String) -> Void {
    var counter = 1

    return {
        print("\(counter). I'm going to \($0)")
        counter += 1
    }
}

即使该counter变量是在travel()内部创建的,它也会被闭包捕获,因此对于该闭包仍将保持活动状态。

因此,如果我们result("XXXX")多次调用,计数器将不断增加:

result("Beijing")
result("Wuhan")
result("Guangzhou")

此时输出结果如下:

1. I'm going to Beijing
2. I'm going to Wuhan
3. I'm going to Guangzhou

总结

  • 1.您可以为变量分配闭包,然后再调用它们。
  • 2.闭包可以接受参数和返回值,例如常规函数。
  • 3.您可以将闭包作为参数传递给函数,并且这些闭包可以具有自己的参数和返回值。
  • 4.如果函数的最后一个参数是闭包,则可以使用尾随闭包语法。
  • 5.Swift会自动提供诸如$0$1的速记参数名称,但并非所有人都使用它们。
  • 6.如果在闭包内部使用外部值,则将捕获它们,以便闭包以后可以引用它们。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容