Swift3.0闭包(Closures)

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

闭包可以捕获和存储其所在上下文中任意常量和变量的引用。 这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift会为您管理在捕获过程中涉及到的内存操作。

函数 中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:

  • 全局函数是有名称但不会捕获任何值的闭包。
  • 嵌套函数是有名称并且可以从其封闭函数中捕获值的闭包。
  • 闭包表达式是用轻量级语法写的没有名称但可以捕获上下文的值。

Swift的闭包表达式拥有简洁的风格,并鼓励在常见场景中以实现语法优化,主要优化如下:

  • 利用上下文推断参数和返回值类型
  • 单表达式(single-expression)闭包可以省略 return 关键字
  • 参数名称简写
  • 后置闭包语法

闭包表达式(Closure Expressions)

闭包表达式是一种利用简洁语法构建内联闭包的方式。 闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。

排序方法(The Sorted Method)

Swift标准库中提供了一个叫做 sorted(by:) 的方法,对会根据提供的值对已知类型的数组进行排序。方法会返回一个已经排序好的数组,但原始的数组不会改变。


The Sorted Method🌰

闭包表达式语法(Closure Expression Syntax)

闭包表达式语法有一般形式:

{ (parameters) -> return type in
    statements
}

在闭包语法表达式中的参数可以作为 in-out 参数,但是不能有默认值。亦可以使用可变参数。元组也可以作为参数和返回值。
上面的🌰也可以这样写:


Closure Expression Syntax🌰

根据上下文推断类型(Interring Type From Context)

因为Swift中有根据上下文推断类型,所以上面的例子可以简写成这样:


Interring Type From Context🌰

单表达式闭包省略 return 关键(Implicit Returns from Single-Expression Closures)

因为Swift的这条特性,所以上面的🌰又可以简化成:


Implicit Returns from Single-Expression Closures🌰

参数名称简写(Shorthand Argument Names)

Swift 自动为内联函数提供了参数名称简写功能,您可以直接通过 $0,$1,$2等名字来引用的闭包的参数的值,所以上面的🌰还可以简化:


Shorthand Argument Names🌰

感觉有点der啊。

运算符函数(Operator Functions)

实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。 Swift的 String 类型定义了关于大于号 (>) 的字符串实现,让其作为一个函数接受两个 String 类型的参数并返回 Bool 类型的值。 而这正好与 sort 函数的第二个参数需要的函数类型相符合。就是说可以这么写:


Operator Functions🌰

后置闭包(Trailing Closures)

如果需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用后置闭包代替。后置闭包在函数结束的括号后面写,尽管如此,它仍是函数的参数。当使用后置闭包语法的时候,不用写参数标签作为函数调用的一部分:


Trailing Closures🌰

所以上面的那个🌰就可以这么写:


还是那个🌰

当后置闭包只需要一个参数的时候,『( )』可以省略:


一直是那个🌰

当闭包非常长以至于不能在一行中进行书写时,后置闭包就变得非常有用。 举例来说,Swift 的 Array 类型有一个 map 方法,其获取一个闭包表达式作为其唯一参数。 数组中的每一个元素调用一次该闭包函数,并返回该元素所映射的值(也可以是不同类型的值)。 具体的映射方式和返回值类型由闭包来指定:

换了个🌰
  • 这个🌰有点复杂,就是当调用map函数的时候,先将16拿出来进行映射。这个后置闭包的参数就是数组中每个元素,返回值是『翻译』之后的字符串。
  • 字典 digitNames 下标后跟着一个叹号 (!),因为字典下标返回一个可选值 (optional value),表明即使该 key不存在也不会查找失败。 在上例中,它保证了 number % 10 可以总是作为一个 digitNames 字典的有效下标 key。 因此叹号可以用于强展开 (force-unwrap) 存储在可选下标项中的 String 类型值。

捕获值(Capturing Values)

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

Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数体内的函数。 嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。


🌰

如果我们单独来看这个函数:

func incrementer() -> Int {
    runningTotal += amount
    return runningTotal
}

一目了然,这个函数并没有传进来任何参数,但是在函数体内访问了 runningTotal 和 amount 变量,这就是因为这个闭包捕获了上下文的值。

由于没有修改 amount 变量,incrementor 实际上捕获并存储了该变量的一个副本,而该副本随着 incrementor 一同被存储。

然而,因为每次调用该函数的时候都会修改 runningTotal 的值,incrementor 捕获了当前 runningTotal 变量的引用,而不是仅仅复制该变量的初始值。捕获一个引用保证了当 makeIncrementor 结束时候并不会消失,也保证了当下一次执行 incrementor 函数时,runningTotal 可以继续增加。

注意:『As an optimization(优化), Swift may instead capture and store a copy of a value if that value is not mutated(突变) by a closure, and if the value is not mutated after the closure is created.』(这句话不知道怎么翻译了。😂)
Swift 同时也处理 runingTotal 变量的内存管理操作,如果不再被 incrementor 函数使用,则会被清除。

makeIncrementr的使用🌰

如果闭包分配给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,将创建一个在闭包和实例间的强引用环。 Swift 使用捕获列表来打破这种强引用环。

闭包是引用类型(Closures Are Reference Types)

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

无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。 上面的例子中,incrementByTen 指向闭包的引用是一个常量,而并非闭包内容本身。

这也就是说如果将闭包赋值给了两个不同的常量(变量),两个值都会指向同一个闭包:


证明🌰

非逃逸闭包(Nonescaping Closures)

当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸

当你定义接受闭包作为参数的函数时,你可以在类型之前标注 『@noescape』,用来指明这个闭包是不允许“逃逸”出这个函数的。将闭包标注 @noescape 能使编译器知道这个闭包的生命周期,从而可以进行一些比较激进的优化。

但是这里需要提一下的就是:闭包只能在函数体中执行,所以编译器明确的知道运行时的上下文:


🌰

这里会报错竟然是因为@noescape 是默认的。在文档中,这段的例子好像默认都是@escape,但是在新的版本中默认是@noescape,不知道是我的理解错误了还是文档的问题。不过问题已经反馈了。

自动闭包(Autoclosures)

这章剩下的部分,等我先看别的再回过头来看。。一脸懵逼ing。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,835评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,900评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,481评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,303评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,375评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,729评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,877评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,633评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,088评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,443评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,563评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,251评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,827评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,712评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,943评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,240评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,435评论 2 348

推荐阅读更多精彩内容