《Swift学习笔记》10 - 闭包的简单介绍
swift介绍:Swift 语言由苹果公司在 2014 年推出,用来撰写 Mac OS 和 iOS 应用程序,并且Swift还是开源的,开源地址:https://github.com/apple/swift,同时附上官方博客地址:https://swift.org
目前Swift已经发展到了第五版本。显然苹果大有推翻 OC “江山”的意思.
今天就来初探一下Swift,看看究竟为何swift有这么大的能力。
参考内容:
Swift中文
YungFan老师
一、闭包的介绍
- 闭包的含义
-
闭包是匿名函数
-
闭包是可以被传递和引用的一个独立模块
-
闭包能够捕获和存储定义在其上下文中的任何常量和变量的引用,这也就是所谓的闭合并包裹那些常量和变量,因此被称为“闭包”
-
闭包和函数一样都是引用类型
-
实际上
函数
和内联函数
都是特殊的闭包。- 全局函数是一个有名字但不会捕获任何值的闭包;
- 内嵌函数是一个有名字且能从其上层函数捕获值的闭包;
- 闭包表达式是一个轻量级语法所写的可以捕获其上下文中常量或变量值的没有名字的闭包。
- 闭包的书写
// 基本书写方式
{ (parameters) -> (return type) in
statements
}
// 案例
{
(闭包的形式参数: String) -> String in
return 闭包的形式参数
}
- 闭包表达式由一对
{}
开始与结束 - 由
in
关键字将闭包分割成两部分:参数与返回值
、闭包体
-
闭包参数
与函数参数
的区别- 形式参数不能提供默认值,其他和函数一样,闭包表达式语法
能够
使用常量形式参数
、变量形式参数
和输入输出形式参数
,但不能提供默认值
。可变形式参数也能使用,但需要在形式参数列表的最后面使用
。元组
也可被用来作为形式参数
和返回类型
。
- 形式参数不能提供默认值,其他和函数一样,闭包表达式语法
二、闭包主要知识点
- 参数名称缩写
- 参数类型可以通过函数类型进行推断:因排序闭包为实际参数来传递给函数,故 Swift 能推断它的形式参数类型和返回类型。
- 在单行闭包的时候,return 关键字可以省略
- 参数名称省略以后,in 关键字也可以被省略
闭包的正常使用
//从数组中筛选指出合适的数据组成新的数组
let block = {
(oneNum : Int) -> Bool in
return oneNum > 10
}
print(block(11)) // true
第一种简写方法:通过类型推倒
返回箭头 ( ->)
和围绕在形式参数名周围的括号()
都可以省略
let blockTwo = {oneNum in return oneNum>10}
print(blockTwo(3)) // false
第二种:闭包的隐式返回 ,省略return
关键字
let blockThree = {numOne in numOne>10}
print(blockThree(12)) // true
第三种简写:参数名称缩写,省略参数声明和in,改为$0
Swift 自动对行内闭包提供简写实际参数名,你也可以通过 1 , $2 等名字来引用闭包的实际参数值。
in
关键字也能被省略,因为闭包表达式完全由它的函数体组成.
let blockFour = {$0>10}
print(blockFour(8)) // false
可以看到 $0
代替了 上面的numOne,并且可以in
给隐藏了
三、尾随闭包
如果你需要将一个闭包表达式
作为函数最后一个实际参数
传递给函数,使用尾随闭包将增强函数的可读性。尾随闭包是一个被书写在函数形式参数的括号外面(后面)的闭包表达式:
func doSomething(name: String, block:(_:String)->Void){
block("尾随闭包 name=" + name)
}
let age = "24"
doSomething(name: "Swift") { (age) in
print(age)
}
doSomething(name: "Swift", block: {s in print(s)})
四、捕获值
- 闭包的一个重要特点就是捕获上下文已被定义的常量和变量,并放到自己的作用域去使用。
下面这个例子说明:
前面说过函数也是一种闭包;这是一个内嵌函数,里面的函数incrementor
可以捕获到主函数的一些参数runningTotal
和amount
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
//这个例子定义了一个叫 incrementByTen 的常量,该常量指向一个每次调用会加 10 的函数。调用这个函数多次得到以下结果:
let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen() // 返回的值为10
incrementByTen() // 返回的值为20
incrementByTen() // 返回的值为30
如果你建立了第二个 incrementer
,它将会有一个新的、独立的 runningTotal
变量的引用:
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// 返回的值为 7
再次调用原来增量器 ( incrementByTen )
继续增加它自己的变量 runningTotal
的值,并且不会影响 incrementBySeven
捕获的变量 runningTotal
值:
incrementByTen() // 返回的值为 40
五、逃逸闭包
- 当闭包
作为一个实际参数
传递给一个函数的时候,我们就说这个闭包逃逸了,因为它可以在函数返回之后被调用。 - 当你声明一个接受闭包作为形式参数的函数时,你可以在形式参数前写
@escaping
来明确闭包是允许逃逸的。
比如说:很多函数接收闭包实际参数来作为启动异步任务的回调。函数在启动任务后返回,但是闭包要直到任务完成——闭包需要逃逸,以便于稍后调用。
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
函数 someFunctionWithEscapingClosure(_:)
接收一个闭包作为实际参数并且添加它到声明在函数外部的数组里。如果你不标记函数的形式参数为 @escaping
,你就会遇到编译时错误。
让闭包 @escaping
意味着你必须在闭包中显式地引用self
,比如说,下面的代码中,传给someFunctionWithEscapingClosure(_:)
的闭包是一个逃逸闭包,也就是说它需要显式地引用 self
。相反,传给 someFunctionWithNonescapingClosure(_:)
的闭包是非逃逸闭包,也就是说它可以隐式地引用self
。
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"
completionHandlers.first?()
print(instance.x)
// Prints "100"
六、自动闭包
- 自动闭包是一种自动创建的用来把作为实际参数传递给函数的表达式打包的闭包。
- 它不接受任何实际参数,并且当它被调用时,它会返回内部打包的表达式的值。
- 通过写普通表达式代替显式闭包而使你省略包围函数形式参数的括号。
- 自动闭包允许你延迟处理,因此闭包内部的代码直到你调用它的时候才会运行。对于有副作用或者占用资源的代码来说很有用,因为它可以允许你控制代码何时才进行求值。
下面的代码展示了闭包如何延迟求值。
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints "5"
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
当你传一个闭包作为实际参数到函数的时候,你会得到与延迟处理相同的行为。
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!"
上边的函数 serve(customer:)
接收一个明确的返回下一个客户名称的闭包。下边的另一个版本的 serve(customer:)
执行相同的任务但是不使用明确的闭包而是通过 @autoclosure
标志标记它的形式参数使用了自动闭包。现在你可以调用函数就像它接收了一个 String 实际参数
而不是闭包。实际参数自动地转换为闭包
,因为 customerProvider
形式参数的类型被标记为 @autoclosure
标记。
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// Prints "Now serving Ewa!"
- 如果你想要自动闭包允许逃逸,就同时使用
@autoclosure
和@escaping
标志。
// customersInLine is ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))
print("Collected \(customerProviders.count) closures.")
// Prints "Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
// Prints "Now serving Barry!"
// Prints "Now serving Daniella!"
闭包的介绍就到这里了。有什么不足的地方欢迎补充。文章大多数内容来自:
求职广告:本人实习生,现在急需一份工作,杭州南京合肥的都可以。对我感兴趣的可以私聊我 0.0。谢谢~~~