zoukankan      html  css  js  c++  java
  • Swift学习-2闭包

    记录学习Swift过程,原网址是:https://www.journaldev.com/15163/swift-closure

    闭包:

    函数也是闭包的一种;闭包是没有名字的函数,也没有标识符func.
    三种形式:全局闭包、嵌套闭包、闭包表达式;
    前两种函数中已经讨论过了;一般我们所提到的闭包都是第三者形式。

    闭包优势:

    1. 闭包比函数简单,swift可以从定义闭包的上下文中推断出参数类型和返回类型,从而便于定义和传递给函数。
    2. 闭包可以用于捕获和存储某个特定时间点的某个变量的状态,并在以后使用它
    3. 闭包允许我们在函数返回后运行一段代码

    定义闭包

    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
    
  • 相关阅读:
    结算凭证中委托付款部分sql
    各公司年资金归集汇总sql
    通过开户银行账号查询客商名称 sql
    TRIGGER_15.8.3BACKUP
    备份触发器:ADDC3
    sql查询单个银行账号重复
    待研究:insert客商账户触发器增加条件提示为空
    sql:劳务统计各分公司管理费用明细合计(等同汇总报表)
    【ASP.NET程序员福利】打造一款人见人爱的ORM(二)
    【ASP.NET程序员福利】打造一款人见人爱的ORM(一)
  • 原文地址:https://www.cnblogs.com/PotatoToEgg/p/14914128.html
Copyright © 2011-2022 走看看