Swift专栏---闭包.初学者可以把它当成OC中block,但是不完全一样.如果你想学好Swift,闭包必须玩转咯!!!喜欢的小伙伴,可以关注我.
我们先不着急玩闭包 先来看看下面的例子:
一个函数嵌套另一个函数
/**
这个方法: 参数为op:String 返回值为:(Int,Int) -> Int (函数:两个参数 (Int) 返回值:Int)
*/
func someFunction(op:String) -> (Int,Int) -> Int{
func plus(a:Int,b:Int) -> Int{
return a+b;
}
func mult(a:Int,b:Int) -> Int{
return a*b;
}
func sub(a:Int,b:Int) -> Int{
return a-b;
}
//定义 一个 (Int,Int) -> Int 函数类型的变量
var result : (Int,Int) -> Int
//switch 分流判别 返回的函数
switch (op) {
case "+":
result = plus
case "*":
result = mult
case "-":
result = sub
default:
result = plus
}
return result
}
//调用
let showPlus:(Int,Int)->Int = someFunction(op: "+")
print("10+20=\(showPlus(10,20))");
let showMult:(Int,Int)->Int = someFunction(op: "*")
print("10*20=\(showMult(10,20))");
上面的例子返回两个子函数,现在稍微变形
func someFunction2(op:String) -> (Int,Int) ->Int{
var result:(Int,Int) -> Int
switch (op) {
case "+":
result = { (a:Int,b:Int) -> Int in
return a+b
}
case "*":
result = {(a:Int,b:Int) -> Int in
return a*b
}
case "-":
result = {(a:Int,b:Int) -> Int in
return a-b
}
default:
result = { (a:Int,b:Int) -> Int in
return a+b
}
}
return result
}
let showPlus2:(Int,Int)->Int = someFunction2(op: "+")
print("10+20=\(showPlus2(10,20))");
let showMult2:(Int,Int)->Int = someFunction2(op: "*")
print("10*20=\(showMult2(10,20))");
/**闭包的基本形式:
result = { (a:Int,b:Int) -> Int in
return a+b
}
{(参数列表)->返回值类型 in
语句
}
*/
闭包写在一对大括号中,用 "in"关键字分隔,"in"后的语句是闭包的主体."in"之前的参数和返回值类型是语句组中所使用的参数和返回值格式的一种指示,并不必在语句组中进行逻辑运算和返回.闭包的表达式的运算结果是一种函数类型,可以作为表达式,函数参数和函数返回值
由于Swift具有类型推断的能力--->于是可以不必显示的写书参数和返回值的类型
{ (a,b) in return a+b }
由于只有一条语句 还可以变形
{ (a,b) in a+b }
Swift 还提供参数名称缩写的功能: 用$0表示第一个参数 $1表示第二个参数
Swift能够根据闭包中使用的参数个数推断出参数列表的定义
此外 in的关键字也可以省略
{ $0+$1 } //完美 真舒服
//使用最简单的模式书写上面的例子
func somFunction3(op:String) -> (Int,Int) -> Int{
var result : (Int,Int) -> Int
switch (op) {
case "+":
result = {$0+$1}
case "*":
result = {$0*$1}
case "-":
result = {$0-$1}
default:
result = {$0+$1}
}
return result
}
- 闭包定义
typealias myClosure = (Int,Int) -> Int
let closureBlock : myClosure = {$0+$1}
closureBlock(10,20)
- 尾随闭包
//尾随闭包: 如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签:
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函数体部分
}
// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure(closure: {
// 闭包主体部分
})
// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
// 闭包主体部分
}
//example(num1,num2){$0+$1} ===>>> example(num1,num2,{$0+$1})
- 逃逸闭包
//当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 @escaping,用来指明这个闭包是允许“逃逸”出这个函数的。
//一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。例如
//回调闭包数组
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
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)
// 打印出 "200"
completionHandlers.first?()
print(instance.x)
// 打印出 "100"
- 自动闭包
//自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。
//我们经常会调用采用自动闭包的函数,但是很少去实现这样的函数。举个例子来说,assert(condition:message:file:line:) 函数接受自动闭包作为它的 condition 参数和 message 参数;它的 condition 参数仅会在 debug 模式下被求值,它的 message 参数仅当 condition 参数为 false 时被计算求值。
//自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行。延迟求值对于那些有副作用(Side Effect)和高计算成本的代码来说是很有益处的,因为它使得你能控制代码的执行时机。下面的代码展示了闭包如何延时求值。
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// 打印出 "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// 打印出 "5"
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// 打印出 "4"
//尽管在闭包的代码中,customersInLine 的第一个元素被移除了,不过在闭包被调用之前,这个元素是不会被移除的。如果这个闭包永远不被调用,那么在闭包里面的表达式将永远不会执行,那意味着列表中的元素永远不会被移除。请注意,customerProvider 的类型不是 String,而是 () -> String,一个没有参数且返回值为 String 的函数。
//将闭包作为参数传递给函数时,你能获得同样的延时求值行为。
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// 打印出 "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))
// 打印 "Now serving Ewa!"
注意 过度使用 autoclosures 会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。
//如果你想让一个自动闭包可以“逃逸”,则应该同时使用 @autoclosure 和 @escaping 属性。@escaping 属性的讲解见上面的逃逸闭包。
// customersInLine i= ["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.")
// 打印 "Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
// 打印 "Now serving Barry!"
// 打印 "Now serving Daniella!"
//在上面的代码中,collectCustomerProviders(_:) 函数并没有调用传入的 customerProvider 闭包,而是将闭包追加到了 customerProviders 数组中。这个数组定义在函数作用域范围外,这意味着数组内的闭包能够在函数返回之后被调用。因此,customerProvider 参数必须允许“逃逸”出函数作用域。
Swift-day1---HelloWord
Swift-day2---基本数据类型
Swift-day3---字符串
持续更新!!!如有什么不足,还请不吝指出!谢谢!
网友评论