闭包是可以在你的代码中被传递和饮用的功能性独立模块。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. Linux(三) 一些命令

    系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...

  2. 适配iOS7之—UITableView和UISearchBar

    iOS7中,如果用UITableViewStyleGrouped的话,里面的cell会比原来的拉长了,这样做应该是为了统一和UITableViewStylePlain风格时cell的大小一致,所以改用 ...

  3. django中日志使用学习记录

    在setting中加入以下代码 LOGGING = { 'version': 1, 'disable_existing_loggers': True, 'formatters': { 'verbose ...

  4. c 数组做为形參时 该參数退化为指针

    当数组做为函数的形參的时候,该參数退化为指针,而且是无法直接求得数组的大小. 传数组给一个函数.数组类型自己主动转换为指针类型,因而传的实际是地址. void func(int array[10]) ...

  5. 两段用来启动/重启Linux下Tomcat的Perl脚本

    两段代码,第二段比较好些. 下面是Split输出结果方式的代码: #!/usr/local/bin/perl #Date:2015-07-07 print "Begin to restart ...

  6. OCP-1Z0-051-题目解析-第16题

    16. Evaluate the following query: SQL> SELECT promo_name q'{'s start date was }' promo_begin_date ...

  7. 负载均衡情况下获取真实ip的方法

    公司用了硬件负载均衡,最近发现日志中的用户ip都为负载均衡器的ip,业务需要所以要改为用户真实ip,下面记录一下! 1.打开文件:/etc/httpd/conf/httd.conf. 2.在文件中查找 ...

  8. linux 文件查找实用技巧

    1.tail catalina.out -n 100000 | grep -niR com.uujimu.utils.ArticleContentReplace.replacNumToA 查找内容,并 ...

  9. 思维探索者:完善个人知识体系的重要性 Google只会告诉你结果

    http://www.nowamagic.net/librarys/veda/detail/1711前面说了,人类解决问题大部分时候会习惯性地使用联想思维,简言之就是首先枚举你关于这个问题能够想到的所 ...

  10. 【强网杯2018】逆向hide

    这是事后才做出来的,网上没有找到现成的writeup,所以在这里记录一下 UPX加壳,而且linux下upx -d无法解,也无法gdb/ida attach 因为是64位,所以没有pushad,只能挨 ...