//
// ViewController5.swift
// swiftT
//
// Created by wjwdive on 2020/5/19.
// Copyright © 2020 wjwdive. All rights reserved.
//
import UIKit
class ViewController5: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "闭包"
// 从sorted(by: ) 函数说起.
let names = ["Jarvis", "Marvis", "Harvis"]
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
//将 backward 方法传递给了 sorted(by: )
var reversedNames = names.sorted(by: backward)
print("常规排序写法 ", reversedNames)
//闭包表达式语法
// 闭包表达式语法能够使用常量形式参数,变量形式参数和输入,输出形式参数,但不能提供默认值。可变形式参数也能使用,但需要在形式参数列表的最后面使用。元组也可被用来作为形式参数和返回类型
/*
{(parameters) -> (return type) in
statements
}
*/
var reversedNamesIn = names.sorted(by: {(s1: String, s2: String) -> Bool in
return s1 > s2
})
print("闭包表达式语法 ", reversedNamesIn)
//从语境中推断类型
// 因为排序闭包为实际参数来传递函数,故 swift 能推断出它的形式参数类型和返回值类型
// sorted(by:) 方法期望它的形式参数是一个(String, String) -> Bool 类型的函数。这意味着(String, String) 和 Bool 类型不需要被写成闭包表达式定义中的一部分,因为所有的类型都能被推断, 返回箭头 (->)和围绕在形式擦拭周围的括号也能被省略
var reversedNameSample = names.sorted(by: {s1, s2 in return s1 > s2})
print("简化闭包语法 去掉参数类型 ,箭头 返回值类型", reversedNameSample)
//从单表达式 闭包隐式返回
// 单表达式闭包能够通过从它的声明中删掉 return 关键字来隐式返回他们单个表达式的结果
var reversedN = names.sorted(by: {s1, s2 in s1 > s2})
print("单表达式闭包 删除return", reversedN)
// 简写是实际参数名
// swift 自动对行内闭包提供简写实际参数名,可以通过 $0, $1, $2 等名字来引用闭包的实际参数值
var rever = names.sorted(by: {$0 > $1})
print("简写实际参数名,用$0,$1 引用实际参数值 删除return", rever)
// 运算符函数
// Swift的String 类型定义了关于大于号(>) 的特定字符传实现, 让其作为一个有两个String类型形式参数的函数并返回一个Bool类型的值。 这正好与 sorted(by:)方法的形式参数需要的函数相匹配。因此,你能简单地传递一个大于号。并且Swift将推断你想使用d大于号特殊字符串函数实现
var re = names.sorted(by: >)
print("极简 sorted(by: >) :", re)
// 尾随闭包
// 如果你需要将一个很长的闭包表达式作为函数的最后一个实际参数传递给函数,使用尾随闭包将增强函数的可读性。尾随闭好是一个被书写在函数形式参数的括号外面(后面)的闭包表达式
var reBack = names.sorted{$0 > $1}
print("尾随闭包 names.sorted{$0 > $1} :", reBack)
// 闭包捕获值
// 一个闭包能够从上下文捕获已被定义的常量和变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍能够在其函数体内引用和修改这些值
//1、 作为一种优化, 如果一个值没有改变 或者在闭包的外面,Swift 可能会使用这个值的拷贝 而不是捕获
//2、 Swift也处理了变量的内存管理操作,但变量不再需要时会被释放
// 创建一个递增器
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
// 内嵌函数是闭包表达式的一种形式(闭包的三种形式之一)
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
print("incrementByTen ", incrementByTen())//10
print("incrementByTen ", incrementByTen())//20
let incrementBy100 = makeIncrementer(forIncrement: 100)
print("incrementBy100 ", incrementBy100())//100
print("incrementByTen ", incrementByTen())//30
// 在swift中, 函数和闭包都是引用类型
// 无论你什么时候赋值一个函数或者闭包个常量或变量,你实际上都是将常量和变量设置为对函数和闭包的引用
let alsoIncrementByTen = makeIncrementer(forIncrement: 10)
print("incrementByTen ", incrementByTen())//40
// 闭包是引用类型
// !!! 如果你分配了一个闭包给实例的属性,并且闭包通过引用该实例或者它的成员来捕获实例,你将在闭包和实例间产生循环引用
func makeIncrementRC(amount: Int) -> () -> Int {
var total = 0
func incrementer() -> Int {
total += amount
return total
}
return incrementer
}
let incrementByTenRC = makeIncrementRC(amount: 10)
print("incrementByTenRC ", incrementByTenRC()) //10
print("incrementByTenRC ", incrementByTenRC()) //20
let incrementBySevenRC = makeIncrementRC(amount: 7)
print("incrementBySevenRC ", incrementBySevenRC()) //7
//逃逸闭包
// 当闭包作为一个实际参数传递给一个函数的时候,并且会在函数返回之后调用,我们就可以说这个闭包逃逸了。当你声明一个接受闭包作为形式参数的函数时,你可以在形式参数前写@escaping 来明确闭包是允许逃逸的
// 闭包可以逃逸的一种方法是被存储在定义与函数外的变量里。比如说,很多函数接受闭包实际参数来作为启动异步任务的回调。函数在启动任务后返回,但是闭包要直到任务完成--闭包需要逃逸,以后便于调用
var customers = ["Jarvis", "Carvis", "Larvis"]
let customerProvider = {customers.remove(at: 0)}
print(customers.count)
print(customerProvider())// Jarvis
print(customers.count)
// 服务商 ,闭包数组,闭包返回 string
var providers:[() -> String] = []
//如果传递给一个函数的闭包参数是在函数执行结束去调用的,要在实际闭包参数类型前面加 @escaping
func collectCustomerProviders(provider: @escaping () -> String) {
providers.append(provider)
}
// 如果不加上 @autoclosure 以下语句报错“ Cannot convert value of type 'String' to expected argument type '() -> String'”
//collectCustomerProviders(provider: customers.remove(at: 0))
// 要想让上句代码OK ,可以把闭包表达式用大括号括起来,或者在 函数定义时,指定为自动给闭包,一个闭包可以同时是自动闭包和逃逸闭包
collectCustomerProviders(provider: {customers.remove(at: 0)})
//func collectCustomerProviders1(provider: @autoclosure @escaping () -> String) {
// providers.append(provider)
//}
}
//闭包的概念
// 全局函数和内嵌函数实际上是特殊的闭包
// 全局函数是一个有名字但不会捕获任何值的闭包
// 内嵌函数是一个有名字且能从其上层函数捕获值的闭包
// 闭包表达式是一个轻量级语法所写的可以捕获其上下文中常量或变量的没有名字的闭包
//
//
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}