Swift 学习笔记 (闭包)
闭包是可以在你的代码中被传递和饮用的功能性独立模块。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 学习笔记 (闭包)的更多相关文章
- 【swift学习笔记】二.页面转跳数据回传
上一篇我们介绍了页面转跳:[swift学习笔记]一.页面转跳的条件判断和传值 这一篇说一下如何把数据回传回父页面,如下图所示,这个例子很简单,只是把传过去的数据加上了"回传"两个字 ...
- Swift学习笔记(一)搭配环境以及代码运行成功
原文:Swift学习笔记(一)搭配环境以及代码运行成功 1.Swift是啥? 百度去!度娘告诉你它是苹果最新推出的编程语言,比c,c++,objc要高效简单.能够开发ios,mac相关的app哦!是苹 ...
- swift学习笔记2——函数、闭包
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- Swift学习笔记(9)--闭包
1.闭包表达式: { (parameters) -> returnType in statements } 注1.闭包表达式语法可以使用常量.变量和inout类型作为参数,不提供默认值. 也可以 ...
- Swift学习笔记一
最近计划把Swift语言系统学习一下,然后将MagViewer用这种新语言重构一次,并且优化一下,这里记录一下Swift的学习笔记. Swift和Objective-C相比,在语法和书写形式上做了很多 ...
- 记录:swift学习笔记1-2
swift还在不断的更新做细微的调整,都说早起的鸟儿有虫吃,那么我们早点出发吧,趁着国内绝大多数的coder们还没有开始大范围普遍应用. 网上有些大神说:swift很简单!我不同意这个观点,假如你用h ...
- swift学习笔记5——其它部分(自动引用计数、错误处理、泛型...)
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- swift学习笔记4——扩展、协议
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- swift学习笔记3——类、结构体、枚举
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
随机推荐
- Python包管理工具pip的基本使用
1.简介 pip 是一个Python包管理工具,主要是用于安装 PyPI 上的软件包,可以替代 easy_install 工具. 2.pip安装 如果你安装的Python 2 >=2.7.9 或 ...
- C# DBHelper类
class DBHelper { //public static string connstr = "server=.;database=SuperKTV;uid=sa;pwd=123456 ...
- Scut游戏服务器引擎6.0.5.1发布
1. 修正缓存删除时不会更新到Redis的问题 2. 修正Model组合3个以上子类时Change事件未绑定的问题 3. 修正中间层MySql与MsSql数据库Sql语句分页问题
- td中内容自动换行
使用<table> 进行页面开发,会遇到字符串很长将table撑开变形的问题,在网上搜了一些,终于找到 一个比较好用的解决方法.贴出来,方便以后使用.在table标签中加入如下的style ...
- HDU 3150 Robot Roll Call – Cambot…Servo…Gypsy…Croooow(map)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3150 Problem Description Mystery Science Theater 3000 ...
- 刘下记录:ImageView.scaleType 属性全解析(含视频)
0. 译序 原文:Working with the ImageView ImageView 有一个经常使用场景:ImageView 的宽度固定,高度等比例缩放.且 ImageView 在 paddin ...
- 【Java编程】JDBC注入攻击-Statement 与 PreparedStatement
在上一篇[Java编程]建立一个简单的JDBC连接-Drivers, Connection, Statement and PreparedStatement我们介绍了怎样使用JDBC驱动建立一个简单的 ...
- GIS可视化——热点格网图
一.简介 原理:按照格网大小将区域进行划分,由一个矩形格网替代当前范围内的数据,由格网中心数字代替格网的权重(可以为格网中数据的数量,数据某权重的平均值.最大值.最小值等), 由格网之间颜色的不同表达 ...
- 通过run configuration启动项目
系统通过配置加载路径是通过classpath加载绝对路径 设置属性选中某个项目,然后在工具栏中选择"Run-->Run Confgurations“,然后在对话框的右边选择" ...
- 【前端GUI】—— 网站美工必须掌握的PS知识点&思维导图
前言:前端离不开与设计的沟通,有时候还需要自己上手改动甚至设计网页,所以这里简单梳理一下近期学习的“网站美工”相关知识及练习.(工作用不上的时候,自己玩儿着也蛮有意思的,哈哈(*゚∀゚*)~) 一.P ...