1.可以理解成OC 中使用的block,但是本质不一样.
2.Swift中使用的是 闭包.
3.闭包是一个代码块.
4.在我们需要的时候执行.
5.闭包可以作为 参数 或者 返回值.
6.闭包内 使用self 注意 循环引用.
7.将“函数、函数指针、闭包”三者对比起来理解,能加深对闭包的理解:
函数: 具有特定功能的代码块;
函数指针: 指向函数的指针;
闭包:除具备“函数和函数指针”的所有功能外, 还包括声明它的上下文(如作用域内的自由变量等).
接下来的闭包定义都是参照函数的定义方式类比下来的, 便于记忆理解.
1.无参数 无返回值的闭包
// 无参数 无返回值的函数
func demoA1(){
func sum(){
print("无参数 无返回值的函数")
}
// 函数的调用 : 函数名()
sum()
}
// 注意: 闭包就是 代码块 也就是{}, 用一个名称来记录它
let closure1 = {}
// 通过自动推导可以得出, 上面的常量closure 的类型为 ()->(), 因次得出:
// 闭包的类型 是 ()->(), 前一个括号里面代表所需要的参数, 后一个括弧里面代表返回值
// 也就是正常的闭包格式: let closure1: ()->() = {}
// 在无参数无返回值的闭包里面 ()-()可以省略不写
// 重点: 因为闭包是代码块, 因此所有的东西都要写在块里面, 这也是与函数的写法的区别!!!
// 类比上面的函数来定义 -> 无参数无返回值的闭包
func demoA2(){
let closure = {()->() in
print("无参数无返回值的闭包")
}
// 闭包的调用
closure()
}
2.有参数 无返回值的闭包
// 有参数 无返回值的函数
func demoB1(){
func sum(a: Int){
print(a)
}
// 函数的调用 : 函数名()
sum(a: 8)
}
// 注意: 因为闭包是代码块, 因此所有的东西都要写在块里面, 这也是与函数的写法的区别!!!
// 规则: 当将闭包的参数或返回值写在块里面的同时, 需要在返回值后面添加 in 来把闭包的参数或者返回值 与 闭包内部的逻辑隔开
// 格式: let closure = {()->() in 闭包内部的代码逻辑}
// 类比上面的函数来定义 -> 有参数无返回值的闭包
func demoB2(){
let closure = {(a: Int)->() in
print(a)
}
// 闭包的调用
closure(3)
}
// 同时, 仿照有参数 无返回值的函数, 有参数无返回值的闭包还有两种写法
func demoB3(){
let closure = {(a: Int)->Void in
print(a)
}
// 闭包的调用
closure(3)
}
// 或者 -- 这是最正常的,也是最多的一种写法
func demoB4(){
let closure = {(a: Int) in
print(a)
}
// 闭包的调用
closure(3)
}
3.有参数 有返回值的闭包
// 有参数 有返回值的函数
func demoC1(){
func sum(a: Int, b: Int) -> Int{
return a + b
}
// 函数的调用 : 函数名()
let result = sum(a: 3, b: 8)
print(result)
}
// 类比上面的函数来定义 -> 有参数有返回值的闭包
func demoC2(){
let closure = {(a: Int, b: Int) -> Int in
return a + b
}
// 闭包的调用
let result = closure(9, 9)
print(result)
}
4. 总结: 闭包的格式
闭包定义的格式总结
闭包定义的格式总结
5.闭包的实际演练(模拟加载数据)
5.1 传递的数据做闭包的参数还是返回值?
/*
1. 情景分析: 模拟加载数据, 当数据加载完成时, 传递数据显示到界面, 而不是一直等待, 体现闭包: 使用的时候才加载的特性, 不需要一直等待.
2. 思考? 传递数据的时候, 数据是作为闭包的参数, 还是返回值???
分析: 当传递的数据作为返回值,
2.1 声明
let closure = { (a: Int) -> Int in
return a
}
2.2 调用
let result = closure(5)
2.3 结果分析
这就相当于 在我传入5的时候得到5, 这不是自己在和自己玩嘛, 我已经是几了, 还调用一下干嘛...再说是外界需要这个数据, 我只是在加载而已, 所以传递的数据做为返回值根本不能满足对外界传递数据的需要.
2.4 结论: 所以传递的数据要作为闭包的参数!
*/
5.2 闭包作为函数参数的写法和使用步骤
/* 写法:
模拟 OC 函数的参数的写法, 实现 Swift 中函数参数的书写
:(int) a OC中正常的数据类型做为参数的写法;
a: Int Swift中正常的数据类型作为参数的写法;
a: ()->() Swift中闭包作为参数的写法
注意: 当闭包(()->())作为参数的时候, 两个小括弧里面只写上数据类型即可.
*/
/* 使用步骤: 3步
1. 在 需要得到数据 的地方 定义和实例化闭包.
2. 在 能够得到数据 的地方, 得到数据的时候, 调用闭包.
3. 在闭包的 块的作用域 中, 得到闭包回调的结果.
*/
5.3 模拟代码
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 1. 在想要得到数据的地方, 定义和实例化闭包
let closure = {(result: String) -> () in
// 3. 得到闭包回调的结果
print(result) // 运行之后, 延迟2面, 打印:不想睡
}
loadData(callBack: closure)
}
// 模拟加载数据
func loadData(callBack: @escaping (String) -> ()){
// 子线程异步加载数据
DispatchQueue.global().async {
// 模拟消耗时间
Thread.sleep(forTimeInterval: 2.0)
// 模拟请求回来的数据, 要传递给viewDidLoad中使用
let data = "不想睡"
// 回到主线程, 刷新页面
DispatchQueue.main.async {
// 2. 在得到数据的时候,执行闭包
callBack(data)
}
}
}
5.4 @escaping关键字
/*
1. 在上一段模拟代码中, 我们发现一个修饰闭包的关键字, @escaping.
2. 不必过度关注, 一般情况下Xcode会自动提示添加该关键字.
3. @escaping的使用时机:
3.1 闭包在其他线程执行 (上一段代码就是在其他线程执行了闭包)
3.2 对传入的闭包进行了赋值操作(包括定义一个新的变量/常量来记录该闭包的时候, 见下面代码)
*/
func loadData(callBack:@escaping (String) -> ()){
let clo = callBack
clo("@escaping关键字")
}
5.5 @escaping关键字 更新一波
6.尾随闭包
就是系统的简写方式,了解即可. 以下两种情况为尾随闭包.
条件1: 定义的函数有且只有一个参数, 并且参数是闭包,函数调用时,( )可以省略.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 1.0 正常写法
let closure = { (result: String) in
}
loadData(callback: closure)
// 1.1 可以演变成
loadData(callback: { (result: String) in
})
// 2.0 尾随闭包写法: 直接写loadData方法, 按回车,自动联想, 原理是->()和里面的参数名称可以省略, 参数类型也可以省略
loadData { (result) in
}
}
func loadData(callback: (String)->()){
}
条件2: 定义的函数有多个参数, 但是最后一个参数为闭包 , 函数的( )提前关闭.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 1.0 正常写法
let closure = { (result: String) in
}
loadData(a: 3, b: "8", callback: closure)
// 1.1 可以演变成
loadData(a: 3, b: "8", callback: { (result: String) in
})
// 2.0 尾随闭包写法: 直接写loadData方法, 按回车,自动联想, 原理是->()里面的参数名称可以省略, ()提前闭合, 只留下闭包了,闭包的参数类型也可以省略
loadData(a: 3, b: "8") { (result) in
}
}
func loadData(a: Int, b: String, callback: (String)->()){
}