//
// ClosureUsage.swift
// SwiftCode
//
// Created by Alisa on 2020/10/10.
// Copyright © 2020 Alisa. All rights reserved.
//
import UIKit
//闭包的用法
/*
闭包:闭包是可以在你的代码中被传递和引用的功能独立代码块。
Swift中的闭包和C以及Objective-C中的blocks很像,还有其他语言中的匿名函数也类似。
特性:闭包能够捕获和存储定义在其上下文中的任何常量和变量的引用,这也就是所谓的闭合和包裹那些常量和变量。
闭包的形式:全局函数是个有名字,但不会捕获任何值的闭包
内嵌函数是一个有名字能从其上层函数捕获值的闭包
闭包表达式是一个轻量级语法所写的可以捕获其上下文中常量或变量值的没有名字的闭包
闭包表达式:
{(parameters)->(return type) in
statements
}
闭包表达式语法能够使用常量形式参数、变量形式参数和输入输出形式参数,但不能提供默认值。
可变形式参数也能使用,但需要在形式参数列表的最后面使用。
元组也可被用来作为形式参数和返回类型
闭包是引用类型:
无论在什么时候赋值一个函数或者闭包给常量或者变量,实际上都是降常量和变量设置为函数和闭包的引用。
逃逸闭包:
当闭包作为一个实际参数传递给一个函数的时候,我们就说这个闭包逃逸了,因为它是在函数返回之后调用的。当声明一个接受闭包作为形式参数的函数时,,
我们可以在形式参数前写 @escaping来明确闭包是允许逃逸的
闭包可以逃逸的一种方法是被储存在定义于函数外的变量里,比如说,很多函数接收闭包实际参数来作为启动异步任务的回调。函数在启动任务后返回,
但是闭包要直到任务完成---闭包需要逃逸,以便稍后调用
**注意:逃逸闭包中,我们必须显示的引用self,非逃逸闭包,,需要隐式的引用self
自动闭包:
Swift语音中还有一种语法,其可以实现对简单闭包的自动生成,这种闭包被称为自动闭包。
需要注意,自动闭包参数的使用有严格的条件,首先此闭包不能够有参数,其次在调用函数传参时,此闭包的实现只能由一句表达式组成,闭包的返回值即
为此表达式的值,自动闭包参数由 @autoclosure来声明
注意:自动闭包默认为非逃逸闭包,如果需要设置为逃逸闭包需要用 @escaping修饰
*/
func somFuntionWithNonescapingClosure(closure:()->Void){
closure()
}
class UseAboutEscapingClosure{
var x = 10
//使用逃逸闭包与非逃逸闭包
func doSomething(){
//逃逸闭包
somFuntionWithNonescapingClosure {
self.x += 100
print("逃逸闭包:self.x = \(self.x)")
}
//非逃逸闭包
somFuntionWithNonescapingClosure {
x += 5
print("非逃逸闭包:x = \(x)")
}
}
//将闭包参数声明为自动闭包
func myFunc2(closure:@autoclosure ()->Bool){
print("将闭包参数声明为自动闭包")
}
//将闭包参数声明为逃逸自动闭包
func myFunc3(closure:@autoclosure @escaping ()->Bool){
print("将闭包参数声明为逃逸自动闭包")
}
}
class ClosureUsage: NSObject
{
//闭包的简单使用与语法
func closureSort() {
let names = ["Anna", "Alex", "Brian", "Jack", "Pre", "sara", "jemmy"]
let reversedNames = names.sorted(by:{(s1 : String, s2 : String) -> Bool in return s1 < s2})
print("reversedNames is \(reversedNames)");
//reversedNames is ["Alex", "Anna", "Brian", "Jack", "Pre", "jemmy", "sara"]
//当把闭包作为行内闭包表达式传递给函数或方法时,形式参数类型和返回类型都可以被推断出来
//单表达式闭包能够通过从它们的声明中删掉return关键字,来隐式返回它们单个表达式的结构。
//上面的也可以写作:
let reversedNames1 = names.sorted(by: {s1, s2 in s1 < s2})
print("reversedNames1 is \(reversedNames1)")
//reversedNames1 is ["Alex", "Anna", "Brian", "Jack", "Pre", "jemmy", "sara"]
//Swift自动对行内闭包提供简写实际参数名,可以通过$0,$1,$2等名字来引用闭包的实际参数值
//如果在闭包表达式中使用这些实际参数名,那么可以在闭包的实际参数列表中忽略对其的定义,并且
//简写实际参数名的数字和类型将会从期望的函数类型中推断出来,in关键字也能被省略,因为闭包表达式完全由它的函数体组成
let reversedNames2 = names.sorted(by: {$0 < $1})
print("reversedNames2 is \(reversedNames2)")
//reversedNames2 is ["Alex", "Anna", "Brian", "Jack", "Pre", "jemmy", "sara"]
}
//运算符函数与闭包
func closureWork1() {
/*
Swift的String类型定义了关于号(>)的特定字符串实现,让其作为一个有两个String类型形式参数的函数,
并返回一个Bool类型的值,因此,这里可以简单的传递一个大于号,并且Swift将推断想使用大括号特殊字符串函数实现
*/
let names = ["Anna", "Alex", "Brian", "Jack", "Pre", "sara", "jemmy"]
let reversedNames3 = names.sorted(by: < )
print("reversedNames3 is \(reversedNames3)")
//reversedNames3 is ["Alex", "Anna", "Brian", "Jack", "Pre", "jemmy", "sara"]
}
//尾随闭包
func closureWork2() {
/*
如果需要将一个很长的闭包表达式作为函数最后一个实际参数传递给函数,使用尾随闭包将增强函数的可读性。
尾随闭包是一个被书写在函数形式参数的括号外面(后面)的闭包表达式
注:如果闭包表达式被用作函数唯一的实际参数,并且闭包表达式用作尾随闭包时,那么调用这个函数的时候,
就不需要在函数名字后边写一对圆括号()
*/
let names = ["Anna", "Alex", "Brian", "Jack", "Pre", "sara", "jemmy"]
let reversedNames = names.sorted(){$0 > $1}
print("reversedNames is \(reversedNames)");
//reversedNames is ["sara", "jemmy", "Pre", "Jack", "Brian", "Anna", "Alex"]
//一个简单的应用例子
let digitNames = [0 : "Zero", 1 : "One", 2 : "Two", 3 : "Three", 4 : "Four", 5 : "Five",
6 : "Six", 7 : "Seven", 8 : "Eight", 9 : "Nine"];
let numbers = [16, 58, 510]
let strings = numbers.map { (number) -> String in
var number = number
var output = ""
repeat{
output = digitNames[number % 10]! + output
number /= 10
}while number > 0
return output
}
print("numbers strings is \(strings)")
//numbers strings is ["OneSix", "FiveEight", "FiveOneZero"]
/*
注:number.map不需要在map后面加任何圆括号,因为map(_:)方法仅仅只有一个形式参数
这个形式参数将以尾随闭包的形式来书写
*/
}
//捕获值
func makeIncrementer(forIncrement amount:Int)->()->Int{
var runningTotal = 0
func incrementer()->Int{
runningTotal += amount
print("当前返回的值 runningTotal = \(runningTotal)")
return runningTotal
}
/*
incrementer()函数是没有任何形式参数,runningTotal和amount不是来自于函数的内部,而是通过捕获主函数runningTotal和amount
把它们内嵌在自身函数内部供使用的,当调用makeIncrementer结束时通过引用捕获来确保不会消失,并确保了在下次再调用incrementer时,
runningTotal将继续增加
*/
return incrementer
}
func closureWork3() {
/*
1 一个闭包能够从上下文捕获已被定义的常量和变量。即使定义一些常量和变量的原作用域已经不存在,闭包仍
能够在其函数体内引用和修改这些值
2 一个能够捕获值的闭包最简单的模型是内嵌函数,即被书写在另一个函数的内部。
一个内嵌函数能够捕获外部函数的实际参数,并且能够捕获任何在外部函数的内部定义了的常量和变量
*/
let incrementByTen = makeIncrementer(forIncrement: 10)
var incrementResult:Int = 0
incrementResult = incrementByTen()
incrementResult = incrementByTen()
incrementResult = incrementByTen()
incrementResult = incrementByTen()
print("多次使用内嵌函数incrementByTen后,最终值:incrementResult = \(incrementResult)")
/*
建立的第二个incrementer,它将会有一个新的、独立的runningTotal变量的引用
*/
let incrementByTwo = makeIncrementer(forIncrement: 2)
var resultTwo:Int = 0
resultTwo = incrementByTwo()
resultTwo = incrementByTwo()
resultTwo = incrementByTwo()
resultTwo = incrementByTwo()
print("多次使用内嵌函数incrementByTwo后,最终值:resultTwo: \(resultTwo)")
/*
闭包是引用类型,在上面的例子中,闭包选择incrementByTen、incrementByTwo指向的是两个不同的常量,而不是闭包它自身的内容
下面这样alsoIncrementByTen会继续incrementByTen上面的结果,它们其实是同一个引用
*/
let alsoIncrementByTen = incrementByTen
let resultThree = alsoIncrementByTen()
print("闭包引用进行简单赋值时,最终值:resultThree = \(resultThree)")
/* 打印信息:
当前返回的值 runningTotal = 10
当前返回的值 runningTotal = 20
当前返回的值 runningTotal = 30
当前返回的值 runningTotal = 40
多次使用内嵌函数incrementByTen后,最终值:incrementResult = 40
当前返回的值 runningTotal = 2
当前返回的值 runningTotal = 4
当前返回的值 runningTotal = 6
当前返回的值 runningTotal = 8
多次使用内嵌函数incrementByTwo后,最终值:resultTwo: 8
当前返回的值 runningTotal = 50
闭包引用进行简单赋值时,最终值:resultThree = 50
*/
}
//逃逸闭包与非逃逸闭包
func useEscapingClosure(){
let instance = UseAboutEscapingClosure()
instance.doSomething()
/* 打印信息
逃逸闭包:self.x = 110
非逃逸闭包:x = 115
*/
}
//自动闭包
func useAutoClosure(){
let instance:UseAboutEscapingClosure = UseAboutEscapingClosure()
//使用非逃逸自动闭包
instance.myFunc2(closure: 1 + 2 + 3 > 10)
//使用逃逸自动闭包
instance.myFunc3(closure: 1 * 3 == 3)
/* 打印信息:
将闭包参数声明为自动闭包
将闭包参数声明为逃逸自动闭包
*/
}
}