{ (parameters) -> returnType in
statements
}复制代码
这里的参数(parameters),能够是in-out(输入输出参数),但不能设定默认值。若是是可变参数,必须放在最后一位,否则编译器报错。元组也能够做为参数或者返回值。
"in"关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。即由in引入函数。api
闭包有三种形式:安全
闭包表达式的优势:bash
简单实例:闭包
let sum = {(a: Int, b: Int) -> Int in
return a + b
}
let s = sum(10, 15)
print(s)
// 从上下文中推导参数和返回值类型
let sum1 = { (a, b) -> Int in
return a + b
}
print(sum1(25, 25))复制代码
概括
闭包类型是由参数类型和返回值类型决定的,和函数是同样的。闭包的类型是(parameters) -> returnTypeide
Swift使用typealias关键字能够为类型起一个别名函数
typealias block = (Int, Int) -> Int
let sum: block = {(a, b) in
return a + b
}
print(sum(10, 15))复制代码
闭包表达式的用法,以sorted方法使用为例ui
// 尾随闭包
let num = [1, 2, 3, 4, 5, 6, 7]
let sortNum = num.sorted(by: {
(a: Int, b: Int) -> Bool in
return a > b
})
print("第一次排序: \(sortNum)")
// 从上下文中推导参数和返回值类型
let sortNum1 = sortNum.sorted(by: {
(a, b) in
return a < b
})
print("第二次排序: \(sortNum1)")
// 单表达式的闭包隐式返回,能够省略return关键字
let sortNum2 = sortNum1.sorted(by: {
(a, b) in
a > b
})
print("第三次次排序: \(sortNum2)")
// 速记参数名,和隐式返回。Swift提供速记参数名关联闭包参数(须要省略参数列表,并根据预期的函数类型推断速记参数名的数量和类型),从$0开始
let sortNum3 = sortNum2.sorted(by: {
$0 < $1
})
print("第四次次排序: \(sortNum3)")
// sorted方法还可使用函数进行排序
func sortNumArr(a: Int, b: Int) -> Bool
{
return a > b
}
let sortNum4 = sortNum3.sorted(by: sortNumArr)
print("第五次次排序: \(sortNum4)")
/*
输出结果
第一次排序: [7, 6, 5, 4, 3, 2, 1]
第二次排序: [1, 2, 3, 4, 5, 6, 7]
第三次次排序: [7, 6, 5, 4, 3, 2, 1]
第四次次排序: [1, 2, 3, 4, 5, 6, 7]
第五次次排序: [7, 6, 5, 4, 3, 2, 1]
*/复制代码
sorted的使用,也能够将闭包写在括号以外。例如:spa
let num = [1, 2, 3, 4, 5, 6, 7]
let sortNum = num.sorted() {
(a: Int, b: Int) -> Bool in
return a > b
}
print("第一次排序: \(sortNum)")复制代码
若将闭包做为函数参数最后一个参数,能够省略参数标签,将闭包表达式写在函数调用括号后面指针
func sum(by: (Int, Int) -> Int) -> Int {
return by(10, 20)
}
let num = sum() {
(a, b) in
return a + b
}
print("和为 \(num)")
func sum1(a: Int, b: Int, by: (Int, Int) -> Int) -> Int{
return by(a, b)
}
let num1 = sum1(a: 10, b: 30) {
(a, b) in
return a + b
}
print("和为 \(num1)")复制代码
闭包能够在其定义的上下文中捕获常量和变量。及时定义这些常量和变量的原始范围再也不存在了,闭包也能够引用并修改这些常量和变量的值。code
func sjzFunc(a: Int) -> () -> Int {
var sjz = 0
func addSjz() -> Int {
sjz += a
return sjz
}
addSjz()
print("sjz的值为:\(sjz), a的值为:\(a)")
return addSjz
}
let sjzF = sjzFunc(a: 100)
print("值为:\(sjzF())")
print("值为:\(sjzF())")
print("值为:\(sjzF())")
print("值为:\(sjzF())")
/*
sjz的值为:100, a的值为:100
值为:200
值为:300
值为:400
值为:500
*/复制代码
func sjzFunc(a: Int) -> () -> Int {
var sjz = 0
// 闭包
let addSjz: () -> Int = {
sjz += a
return sjz
}
addSjz()
print("sjz的值为:\(sjz), a的值为:\(a)")
return addSjz
}
let sjzF = sjzFunc(a: 100)
print("值为:\(sjzF())")
print("值为:\(sjzF())")
print("值为:\(sjzF())")
print("值为:\(sjzF())")
/*
sjz的值为:100, a的值为:100
值为:200
值为:300
值为:400
值为:500
*/复制代码
能够看出,当调用addSjz的时候,sjz的值也改变了
addSjz捕获对变量sjz和参数a的引用,经过引用捕获确保sjzFunc结束时,sjz和a不会消失。而且确保了下次调用sjzF时,sjz可用。当它们再也不须要时,Swift处理变量的全部内存管理。
函数和闭包是引用类型。也就是说,当给变量和常量赋值闭包或函数时,变量和常量被赋值为闭包或函数的引用。
例如:
let sjzFF = sjzF // sjzFF和sjzF引用的是同一个闭包复制代码
当闭包做为参数传递给函数时,闭包被称为转义函数,在函数返回后调用。
声明一个将闭包做为一个参数的函数时,能够在参数类型以前编写@escaping,以指示容许闭包被转义。
闭包能够逃避的方法之一是存储在函数外定义的变量中。
var sjzBlock: () -> Void = {
}
func someEscapingFunc(com: @escaping () -> Void){
print("执行函数")
sjzBlock = com
}
func someNoneFunc(com: () -> Void){
com()
}
class someClass {
var x = 10
func doSomeThint() -> Void {
someEscapingFunc {
self.x = 20
}
someNoneFunc {
x = 30
}
}
}
let some = someClass()
some.doSomeThint()
print(some.x)
sjzBlock()
print(some.x)
/*
输出
执行函数
30
20
*/复制代码
用@escaping标记一个闭包意味着你必须在闭包中self引用。 例如,someEscapingFunc的闭包是一个转义闭包,这意味着它须要self引用。 相反,封闭传递给someNoneFunc是一个非转义的闭包,这意味着它能够隐式引用。
@escaping和@non-escaping修饰符是用来修饰闭包的。闭包默认为@non-escaping类型
@escaping:转义闭包,闭包的生命周期不在传入的函数范围内管理。由函数外部变量持有。当函数结束以后,闭包才去执行
@non-escaping:闭包在函数内执行完后,函数才去执行,闭包销毁。
当使用@escaping时,要考虑self被循环引用。
缘由参考OC的block,解决方法也相似。
Dog类
import Cocoa
class sjzDog: NSObject {
var eatFull: ((String) -> Void) = {(String) in
}
var food: String?
override init() {
super.init()
}
deinit {
print("狗挂了")
}
func eat(food: String, by: @escaping (String) -> Void) -> Void {
self.food = food
print("我吃了:\(food)")
eatFull = by
}
func eatFinish() -> Void {
eatFull(food!)
}
}复制代码
People类
import Cocoa
class sjzPeople: NSObject {
let dog: sjzDog
var name: String?
init(name: String) {
self.name = name
dog = sjzDog()
super.init()
}
deinit {
print("人挂了")
}
func feed(food: String) -> Void {
// 循环应用
// 输出:
// 我吃了:milk
// 我吃了milk, 我吃饱了,我想和主人Tom玩耍
// dog.eat(food: food) { (a) in
// print("我吃了\(a), 我吃饱了,我想和主人\(self.name!)玩耍")
// }
/*如下输出:
我吃了:milk
我吃了milk, 我吃饱了,我想和主人Tom玩耍
人挂了
狗挂了
*/
// 1. 解决循环引用
// weak var weakSelf = self
// dog.eat(food: food) { (a) in
// print("我吃了\(a), 我吃饱了,我想和主人\(weakSelf!.name!)玩耍")
// }
// 2. 解决循环引用
// dog.eat(food: food) { [unowned self] (a) in
// print("我吃了\(a), 我吃饱了,我想和主人\(self.name!)玩耍")
// }
// 3. 解决循环引用
dog.eat(food: food) { [weak self] (a) in
print("我吃了\(a), 我吃饱了,我想和主人\(self!.name!)玩耍")
}
dog.eatFinish()
}
}复制代码
调用
var people: sjzPeople? = sjzPeople.init(name: "Tom")
people!.feed(food: "milk")
people = nil复制代码
循环引用缘由:
dog类中引用了闭包eatFull,people类中引用了dog类,闭包eatFull又引用了people,造成了闭环
在dog类中继续声明变量和方法:
var play: (() -> Void)?
func playOrFood() -> Void {
play = {
print("我要玩, 我不想吃 \(String(describing: self!.food))")
}
play!()
}复制代码
在调用playOrFood方法后,将dog对象置空,dog对象不能释放,造成了循环引用
缘由:
dog类引用了paly闭包,paly闭包又引用了dog类。解决方法和上面dog类和people互相引用相似
解决方法:
weak表示可用范围内self为弱引用unowned表示可用范围内self都为assign,不会强引用,若是对象释放,指针地址依然存在,不安全