记录学习Swift过程,原网址是:https://www.journaldev.com/15163/swift-closure
闭包:
函数也是闭包的一种;闭包是没有名字的函数,也没有标识符func.
三种形式:全局闭包、嵌套闭包、闭包表达式;
前两种函数中已经讨论过了;一般我们所提到的闭包都是第三者形式。
闭包优势:
- 闭包比函数简单,swift可以从定义闭包的上下文中推断出参数类型和返回类型,从而便于定义和传递给函数。
- 闭包可以用于捕获和存储某个特定时间点的某个变量的状态,并在以后使用它
- 闭包允许我们在函数返回后运行一段代码
定义闭包
1.基本语法:
{}:代表闭包主体
(parameters) : 入参及参数类型
-> (return type) : 返回与返回值类型
in : 将参数和返回类型与主体分开。
{(parameters) -> (return type) in
}
2.定义一个闭包变量
var myClosure : (ParameterTypes) -> ReturnType
示例
1.定义一个函数和一个闭包,无参无返回
打印Hello world
//函数
func helloWorldFunc(){
print("Hello world")
}
//闭包
var helloWorldClosure = {() -> () in
print("Hello world")
}
当我们不需要有返回值,或者返回值类型可以从上下文推断出来,我们可以省略返回部分的代码;
当我们没有入参,或者入参类型可以从上下文推断出来时,我们也可以省略参数部分的代码
所以上面的闭包可以简化为:
var helloWorldClosureSimply = {
print("Hello world")
}
2.定义一个有参有返回的函数和闭包
将两个字符串拼接起来
func appendString(_ a:String,with b:String) -> String{
return a + " : " + b
}
print(appendString("Swift", with: "Functions")) //打印结果: "Swift : Functions"
var appendStringClosure = {(a: String,b : String) -> String in
return a + " : " + b
}
print(appendStringClosure("Swift","Closure"))//打印结果:Swift : Closure
//由于swift可以从上下文中推断出入参与返回值类型,上面的闭包可以变换成以下几种格式
//从入参类型,推断出返回值类型,所以可以省略返回值类型
var appendStringClosureWithOutReturnType = {(a:String,b:String) in
return a + " : " + b
}
var appendStringClosureTypeDeclaredOnTheVariable : (String,String) -> String = {(a,b) in
return a + " : " + b //也可以省略return关键字
}
//Swift允许我们使用简写的参数名称来引用传递给closure的参数:分别为第一个、第二个、第三个参数引用$0、$1、$2等参数。
var appendStringUseShortHandName : (String,String) -> String = {
$0 + " : " + $1
}
var appendStringUseShortHandNameTakeOneOrMoreAhead = {
$0 + " : " + $1 + " : " + $2
}
print(appendStringUseShortHandNameTakeOneOrMoreAhead("Swift", "Closures", "Awesome")) //打印结果: "Swift : Closures Awesome"
3.创建一个用函数或闭包做参数的函数
func operationsSq(a:Int,b:Int,myFunction:(Int,Int) -> Int) -> Int{
return myFunction(a,b)
}
//创建函数作为参数也是可以实现,但是会增加样板代码
func addSquareFunc(_ a:Int,_ b:Int) -> Int{
return a*a + b*b
}
operationsSq(a:2,b:4,myFunction:addSquareFunc)//打印结果:20
//下面使用闭包
var addSquareClosure : (Int,Int) -> Int = {$0 * $0 + $1 * $1}
var subtractSquareClosure: (Int, Int) -> Int = { $0*$0 - $1*$1 }
var multiplySquareClosure: (Int, Int) -> Int = { $0*$0 * $1*$1 }
var divideSquareClosure: (Int, Int) -> Int = { ($0*$0) / ($1*$1) }
var remainderSquareClosure: (Int, Int) -> Int = { ($0*$0) % ($1*$1) }
operationsSq(a:4,b:2,myFunction:addSquareClosure)//打印结果:20
operationsSq(a:4,b:2,myFunction:subtractSquareClosure)//打印结果:12
operationsSq(a:4,b:2,myFunction:multiplySquareClosure)//打印结果:64
operationsSq(a:4,b:2,myFunction:divideSquareClosure)//打印结果:4
operationsSq(a:4,b:2,myFunction:remainderSquareClosure)//打印结果:0
因为Swift中表明,如果闭包中的入参和返回值可以从上下文中判断出来时,可以省略入参和返回,所以将上面定义的闭包变量进行了如下尝试:
var add = {$0 * $0 + $1 * $1}
但是一直报错 错误原因为:Ambiguous use of operator '*';了解了一下,因为没有确认入参类型和出参类型,结合上下文也不能确认类型。所以改为下面的代码,就不报错了,所以不确认输入或输出类型时,body体中一定要给出一个有类型的数据。
var add = {$0 * $0 * + $1 * $1 * 1}
言归正传。然后也可以不定义闭包变量,直接写在调用函数中
operationsSq(a:2,b:2, myFunction: { $0*$0 + $1*$1 })
operationsSq(a:4,b:5, myFunction: { $0*$0 - $1*$1 })
operationsSq(a:5,b:5, myFunction: { $0*$0 * $1*$1 })
operationsSq(a:10,b:5, myFunction: { ($0*$0) / ($1*$1) })
operationsSq(a:7,b:5, myFunction: { ($0*$0) % ($1*$1) })
如果闭包在函数末尾,则函数也可以简写如下:
operationsSq(a:2,b:2){
($0*$0) + ($1*$1)
}
下面看一个例子,我们将用闭包表达式将指数相加
func sumOfExponentials(from:Int,to:Int,myClosure:(Int)->Int) -> Int{
var sum = 0
for i in from...to{
sum = sum + myClosure(i)
}
print(sum)
return sum
}
sumOfExponentials(from:0,to:5){$0} //0-5相加之和
sumOfExponentials(from:0,to:5){$0 * $0}//0-5平方之和
sumOfExponentials(from:0,to:5){$0 * $0 * $0}//0-5立方之和
4.map、sorted也是一种尾随闭包
//将整数型数组转变为字符型数组
var numbersArray = [1,2,3,4,5,6]
var closureString = numbersArray.map{
return "($0)"
}
print(closureString)//打印结果:["1", "2", "3", "4", "5", "6"]
//使用尾随闭包按降序排列数组
var descendingArray = numbersArray.sorted{$0 > $1}
print(descendingArray)//打印结果:[6, 5, 4, 3, 2, 1]
5.捕获闭包中的列表
var y = 0
var myClosure = {
print("The value of x is start of (y)")
}
myClosure()//打印结果:The value of x is start of 0
//但是当我们重新为 y 赋值时,
y = 5
myClosure()//打印结果:The value of x is start of 5
myClosure定义和初始化都在y = 5 赋值之前,为什么结果是The value of x is start of 5?因为闭包捕获的是y的引用(内存地址)。对该内存地址的值所做的任何更改都将在调用它时由闭包显示。要使x表现为值类型,我们需要使用Capture Lists。Capture Lists是一个数组[],它保存变量的本地副本。
//修改如下:
var variable = 5
var capturesClosure = { [variable] in
print("The value of variable is start of (variable)")
}
capturesClosure()
variable = 10
capturesClosure()
此时再改变变量的值,闭包中的值也不会改变;
5.防止强引用造成内存泄露示例
捕获引用类型在类中使用时可能是破坏性的,因为闭包可能持有对实例变量的强引用,并导致内存泄漏。我们来看一个例子
class Person {
var x: Int
var myClosure: ()->() = {print("Hey there")}
init(x: Int)
{
self.x = x
}
func initClosure()
{
myClosure = { print("Initial value is not defined yet")}
}
deinit{
print("(self) escaped")
}
}
var a:Person? = Person(x: 0)
a?.initClosure()
a?.x = 5
a?.myClosure()
a = nil
打印结果:Initial value is not defined yet __lldb_expr_12.Person escaped
类正常释放
class Person {
var x: Int
var myClosure: ()->() = {print("Hey there")}
init(x: Int)
{
self.x = x
}
func initClosure()
{
myClosure = { print("Intial value is (self.x)")}
}
deinit{
print("(self) escaped")
}
}
var a:Person? = Person(x: 0)
a?.initClosure()
a?.x = 5
a?.myClosure()//打印结果:Intial value is 5
a = nil
deinit中未打印,因为闭包强引用了self.x,修改如下:
class Person {
var x: Int
var myClosure: ()->() = {print("Hey there")}
init(x: Int)
{
self.x = x
}
func initClosure()
{
myClosure = {[weak self] in guard let weakSelf = self else { return }
print("Intial value is (weakSelf.x)")}
}
deinit{
print("(self) escaped")
}
}
var a:Person? = Person(x: 0)
a?.initClosure()
a?.x = 5//打印结果:Intial value is 5
a?.myClosure()
a = nil//打印结果:__lldb_expr_16.Person escaped
因为弱引用是这样一种引用,它不会对它所引用的实例保持强持有,因此不会阻止ARC处理被引用的实例.
6.逃逸(转义)闭包
1.转义闭包是在传递给它的函数返回后调用的闭包。换句话说,它比传递给它的函数更长寿。转义闭包通常用于完成处理程序,因为它们在函数结束后被调用。
2.非转义闭包是在传递给它的函数中调用的闭包,即在它返回之前。默认情况下,闭包是不可转义的
var completionHandlers: [() -> Void] = []
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure {[weak self] in guard let weakSelf = self else { return }
weakSelf.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
deinit{
print("deinitalised")
}
}
var s:SomeClass? = SomeClass()
s?.doSomething()
print(s?.x ?? -1) //prints 200
completionHandlers.first?()
print(s?.x ?? -1) //prints 100
s = nil