闭包是可以在你的代码中被传递和饮用的功能性独立模块。Swift中的闭包和C以及Objective-C中的Block很像,和其他语言中的匿名函数也很像。

闭包能捕获和存储定义在其上下文中的任何常量和变量的饮用,这也就是所谓的闭合并包裹那些常量和变量,因此称为闭包,Swift能够为你处理所有关于捕获内存管理的操作。

在上一篇函数的介绍中 全局和内嵌函数 实际上就是特殊的闭包,闭包符合如下三种形式中的一种

全局函数是一个有名字但不会捕获任何值的闭包

内嵌函数是一个有名字且能从7其上层函数捕获值的闭包

闭包表达式是一个轻量级语法所写的可以捕获其上下文中常量货变量值的没有名字的闭包

Swift 的闭包表达式拥有简洁的风格,鼓励在常见场景中实现简洁,无累赘的语法。常见的优化包括:

利用上下文推断形式参数和返回值的类型;
单表达式的闭包可以隐式返回;
简写实际参数名;
尾随闭包语法。

Sorted方法

Swift 的标准库提供了一个叫做 sorted(by:) 的方法,会根据你提供的排序闭包将已知类型的数组的值进行排序。一旦它排序完成, sorted(by:) 方法会返回与原数组类型大小完全相同的一个新数组,该数组的元素是已排序好的。原始数组不会被 sorted(by:) 方法修改。

下面这个闭包表达式的栗子使用 sorted(by:) 方法按字母排序顺序来排序一个 String 类型的数组。这是将被排序的初始数组:

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

闭包的表达式语法

//闭包的表达式语法
{(parameters) ->(return type) in
statements
}

闭包表达式语法能够使用常量形式参数、变量形式参数和输入输出形式参数,但不能提供默认值。可变形式参数也能使用,但需要在形式参数列表的最后面使用。元组也可被用来作为形式参数和返回类型。

下面的例子和上面的例子效果是一样的。

let names = ["Chris","Alex","Ewa","Barry","Daniella"]

names.sorted(by: {(s1:String,s2:String) -> Bool in
return s1 > s2
})

需要注意的是行内闭包的形式参数类型和返回类型的声明与 backwards(_:_:) 函数的申明相同。在这两个方式中,都书写成 (s1: String, s2: String) -> Bool。总之对于行内闭包表达式来说,形式参数类型和返回类型都应写在花括号内而不是花括号外面。

闭包的函数整体部分由关键字 in 导入,这个关键字表示闭包的形式参数类型和返回类型定义已经完成,并且闭包的函数体即将开始。

从语境中推断类型

因排序闭包为实际参数来传递给函数,故 Swift 能推断它的形式参数类型和返回类型。 sorted(by:) 方法期望它的第二个形式参数是一个 (String, String) -> Bool 类型的函数。这意味着 (String, String)和 Bool 类型不需要被写成闭包表达式定义中的一部分,因为所有的类型都能被推断,返回箭头 ( ->) 和围绕在形式参数名周围的括号也能被省略:

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

从单表达式闭包隐式返回

单表达式闭包能够通过从它们的声明中删掉 return 关键字来隐式返回它们单个表达式的结果,前面的栗子可以写作:

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

尾随闭包

如果你需要将一个很长的闭包表达式作为函数最后一个实际参数传递给函数,使用尾随闭包将增强函数的可读性。尾随闭包是一个被书写在函数形式参数的括号外面(后面)的闭包表达式:

func someFunctionThatTakesAClosure(closure:()->()){

}

调用

someFunctionThatTakesAClosure {
print("heihei")
}

具体例子

func loadData(completion:@escaping(_ result:[String])->()){
//耗时操作
DispatchQueue.global().async {
print("耗时操作")
} let json = ["闭包","传值","Demo"] //回到主线程更新UI
DispatchQueue.main.async {
print("json数据 \(json)")
completion(json)
}
} loadData { (result) in
print("直接操作 \(result)")
}

捕获值

一个闭包能从上下文中捕获已被定义的常量或者变量,即使定义这些常量和变量的原作用语已经不存在,闭包仍能够在其函数体内引用和修改这些值。

在Swift中,一个能捕获值的闭包最简单的模型是内嵌函数。例子

func makeIncrementer(forincrementer amount:Int) ->()->Int {
var runingTotal =
func incrementer()->Int {
runingTotal += amount
return runingTotal
}
return incrementer
} makeIncrementer(forincrementer: )()

incrementer() 函数是没有任何形式参数, runningTotal 和 amount 不是来自于函数体的内部,而是通过捕获主函数的 runningTotal 和 amount 把它们内嵌在自身函数内部供使用。当调用 makeIncrementer  结束时通过引用捕获来确保不会消失,并确保了在下次再次调用 incrementer 时, runningTotal 将继续增加

注意:作为一种优化 如果一个值没有改变或者在闭包的外面 Swift可能会使用这个值的拷贝而不是捕获。

Swift也处理了变量的内存管理操作,当变量不再需要时会被释放。

逃逸闭包

当闭包作为一个实际参数传递给一个函数的时候,我们就说这个闭包逃逸了。因为它可以在函数返回之后被调用。当你声明一个接受闭包作为形式参数的函数时,你可以在这个形式参数前写 @escaping来明确闭包时允许逃逸的。

闭包可以逃逸的一种方式是被存储在定义函数外的变量里。例如

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

函数 someFunctionWithEscapingClosure(_:) 接收一个闭包作为实际参数并且添加它到声明在函数外部的数组里。如果你不标记函数的形式参数为 @escaping ,你就会遇到编译时错误。

让闭包 @escaping 意味着你必须在闭包中显示的引用self 比如在下面的代码中,传给someFunctionWithEscapingClosure(_:) 的闭包是一个逃逸闭包,也就是说它需要显式地引用 self 。相反,传给 someFunctionWithNonescapingClosure(_:) 的闭包是非逃逸闭包,也就是说它可以引式地引用 self 。

var completionHandlers:[() ->Void] = []
func someFunctionWithEscapingClosure(completionHandler:@escaping ()->Void) {
completionHandlers.append(completionHandler)
} func someFunctionWithNoescapingClosure(closure:() ->Void) {
closure()
}
class someClass {
var x =
func doSomething() {
someFunctionWithNoescapingClosure {
x =
}
someFunctionWithEscapingClosure {
self.x =
}
}
}

自动闭包

自动闭包是一种自动创建的用来把作为实际参数传递给函数的表达式打包的闭包 它不接受任何实际参数,并且当他被调用的时候,他会返回内部打包的表达式的值。这个语法的好处在与通过写普通表达式替代显示闭包而使你省略包围函数形式参数的括号。

调用一个带有自动闭包的函数是很常见的,但实现这类函数就不那么常见了。比如说, assert(condition:message:file:line:) 函数为它的 condition  和 message 形式参数接收一个自动闭包;它的 condition 形式参数只有在调试构建是才评判,而且 message 形式参数只有在 condition 是 false 时才评判。

自动闭包允许你延迟处理,因此闭包内部的代码直到你调用它的时候才会运行。对于有副作用或者占用资源的代码来说很有用,因为它可以允许你控制代码何时才进行求值。下面的代码展示了闭包如何延迟求值。

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine) let customerProvider = {customersInLine.remove(at: )}
print(customersInLine.count)// print("now serving \(customerProvider())!")
customersInLine.count //

尽管 customersInLine 数组的第一个元素以闭包的一部分被移除了,但任务并没有执行直到闭包被实际调用。如果闭包永远不被调用,那么闭包里边的表达式就永远不会求值。

当你传一个闭包作为实际参数到函数的时候,你会得到与延迟处理相同的行为。

如果你想要自动闭包允许逃逸,就同时使用 @autoclosure 和 @escaping 标志。

这些只是闭包的一些简单概念和简单例子,在实际应用中还要注意它们的内存管理等等。会在后面的与oc中的block对比中进行学习。敬请关注。

Swift 学习笔记 (闭包)的更多相关文章

  1. 【swift学习笔记】二.页面转跳数据回传

    上一篇我们介绍了页面转跳:[swift学习笔记]一.页面转跳的条件判断和传值 这一篇说一下如何把数据回传回父页面,如下图所示,这个例子很简单,只是把传过去的数据加上了"回传"两个字 ...

  2. Swift学习笔记(一)搭配环境以及代码运行成功

    原文:Swift学习笔记(一)搭配环境以及代码运行成功 1.Swift是啥? 百度去!度娘告诉你它是苹果最新推出的编程语言,比c,c++,objc要高效简单.能够开发ios,mac相关的app哦!是苹 ...

  3. swift学习笔记2——函数、闭包

    之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...

  4. Swift学习笔记(9)--闭包

    1.闭包表达式: { (parameters) -> returnType in statements } 注1.闭包表达式语法可以使用常量.变量和inout类型作为参数,不提供默认值. 也可以 ...

  5. Swift学习笔记一

    最近计划把Swift语言系统学习一下,然后将MagViewer用这种新语言重构一次,并且优化一下,这里记录一下Swift的学习笔记. Swift和Objective-C相比,在语法和书写形式上做了很多 ...

  6. 记录:swift学习笔记1-2

    swift还在不断的更新做细微的调整,都说早起的鸟儿有虫吃,那么我们早点出发吧,趁着国内绝大多数的coder们还没有开始大范围普遍应用. 网上有些大神说:swift很简单!我不同意这个观点,假如你用h ...

  7. swift学习笔记5——其它部分(自动引用计数、错误处理、泛型...)

    之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...

  8. swift学习笔记4——扩展、协议

    之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...

  9. swift学习笔记3——类、结构体、枚举

    之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...

随机推荐

  1. POJ 3268 Silver Cow Party (Dijkstra + 优先队列)

    题意:由n个牧场,编号1到n.每个牧场有一头牛.现在在牧场x举办party,每头牛都去参加,然后再回到自己的牧场.牧场之间会有一些单向的路.每头牛都会让自己往返的路程最短.问所有牛当中最长的往返路程是 ...

  2. poj1185 [NOI2001]炮兵阵地

    http://poj.org/problem?id=1185 三维装压dp,压缩上一行状态与本行状态,枚举上两行状态转移 第一维可以滚掉,实际复杂度只枚举符合情况的情况,每行状态不会超过60并非$2^ ...

  3. NOI模拟题4 Problem C: 填格子(board)

    Solution 首先我们要有敏锐的直觉: 我们将每一列中不选哪种颜色看作是一个序列, 则我们发现这个序列要求相邻两位的颜色不同. 我们还发现, 一个这样的序列对应两种不同的合法的棋盘, 因此统计合法 ...

  4. js 面试的坑:变量提升

    全局中的解析和执行过程 预处理:创建一个词法环境(LexicalEnvironment,在后面简写为LE),扫描JS中的用声明的方式声明的函数,用var定义的变量并将它们加到预处理阶段的词法环境中去. ...

  5. AMD 的 CommonJS wrapping

    其实本文的标题应该是「为什么我不推荐使用 AMD 的 Simplified CommonJS wrapping」,但太长了不好看,为了美观我只能砍掉一截. 它是什么? 为了复用已有的 CommonJS ...

  6. ASIHTTPRequest, request sent twice

    ´ve just started using ASIHTTPRequest for iOs and I have a small issue with it. All requests are sen ...

  7. If Value Exists Then Query Else Allow Create New in Oracle Forms An Example

    An example given below for Oracle Forms, when a value exists then execute query for that value to di ...

  8. Batch update returned unexpected row count from update [0];

    Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested ...

  9. bzoj1061【NOI2008】志愿者招募

    1061: [Noi2008]志愿者招募 Time Limit: 20 Sec  Memory Limit: 162 MB Submit: 2740  Solved: 1703 [Submit][id ...

  10. appium在MAC上环境搭建

    1. 安装.启动Appium bixiaopeng@bixiaopeng ~$ npm install -g appium Password: npm http GET https://registr ...