当前位置: 代码迷 >> 综合 >> Day12:闭包
  详细解决方案

Day12:闭包

热度:106   发布时间:2023-09-29 19:04:55.0
  • 闭包的概念
  1. 闭包是可以在你的代码中被传递和引用的功能性独立代码块。
  2. 闭包能够捕获和存储定义在其上下文的任何常量和变量的引用,这也就是所谓的闭合并包裹那些常量和变量,因此被称为“闭包”,swift能够为你处理所能关于捕获的内存管理的操作。
  • 在函数章节中有介绍的全局和内嵌函数,实际上是特殊的闭包。闭包符合如下三种形式的一种在
  1. 全局函数是一个有名字但不会捕获任何值的闭包;
  2. 内嵌函数是一个有名字且能从上层函数捕获的闭包;
  3. 闭包表达式是一个轻量级语法所能写的可以捕获其上下文中常量或变量的没有名字的闭包。
  • 闭包表达式
  1. 闭包表达式是一种在简短行内就能写完闭包的语法。
  2. 闭包表达式从sorted(by:)函数说起
  3. swift的标准库提供了一个叫做sorted(by:)的方法,会根据你提供的排序闭包将已知类型数数组的值进行排序。一旦它排序完成,sorted(by:)方法会返回与原数组类型大小完全相同的一个新数组,该数组的元素是已排序好的。原始数组组不会sorted(by:)方法修改。
  • 闭包表达式语法
  1. 闭包表达式语法能够使用常量形式参数、变量形式参数和输入输出形式参数,但不能提供默认值。可变形式参数也能使用,但需要在形式参数列表的最后面使用。元组也可以被用来作为形式参数和返回类型。
  • 闭包表达式语法版本的backward
  1. 将之前backward(_:_:)函数改为闭包表达版本。
  • 从语境中推断类型
  1. 因排序闭包为实际参数来传递给函数,故swift能推断它的形式参数类型和返回类型。
  2. Sorted(by:)方法期望它的形式参数是一个(string,string) ->bool类型的函数。这意味着(string,string)和bool 类型不需要被写成闭包表达式定义中的一部分,因为所有的类型都被推断,返回箭头->和围绕在形式参数名周围的括号也能被省略。
  • 从单表达式闭包隐式返回
  1. 单表达式闭包能够通过他们的声明中删除retune关键字来隐式返回他们单个表达式的结果。
  • 简写实际参数名
  1. swift自动对行内包提供简写实际参数名,可以通过$0,$1,$2等名字来引用闭包实际参数值。
  • 运算符函数
  1. swift的string类型定义关于大于号(>)特定字符串实现,让其作为一个有两个string类型形式参数需要的函数相匹配。因此,你能简单地传递一个大于号,并且swift将推断你想使用大于号特殊字符串函数实现。
  • 尾随闭包
  1. 如果你需要将一个很长的闭包表达式作为函数最后一个实际参数传递给函数,使用尾随闭包将增强函数的可读性。尾随闭包是一个被书写在函数形式的括号外面(后面)的闭包表达式。
  • 【代码演示】
    import UIKit//1、sorted()说闭包
    let names = ["zhangsan","lisi","wangwu","zhaoliu"]
    let sortedNames = names.sorted()//正序
    print(sortedNames)//结果:["lisi", "wangwu", "zhangsan", "zhaoliu"]
    let subNames = ["zhangsan","lisi","wangwu","zhaoliu"]func backwoard(_ s1:String,_ s2:String) ->Bool{return s1 > s2
    }let subSortedNames = subNames.sorted(by: backwoard)//逆序
    print(subSortedNames)//结果:["zhaoliu", "zhangsan", "wangwu", "lisi"]let newSortedNames = names.sorted(by: {(s1:String,s2:String) ->Bool in return s1 > s2})
    print(newSortedNames)//结果//2、从语境中推断类型let otherSortedNames = names.sorted(by: {s1,s2 in return s1 > s2})
    print(otherSortedNames)//结果:["zhaoliu", "zhangsan", "wangwu", "lisi"]//3、从单表达式闭包隐式返回
    let anotherSortedNames = names.sorted(by: {s1,s2 in s1 > s2})
    print(anotherSortedNames)//结果:["zhaoliu", "zhangsan", "wangwu", "lisi"]
    //4、简写实际参数名
    let simlpSortedNames = names.sorted(by: {$0 > $1})
    print(simlpSortedNames)//结果:["zhaoliu", "zhangsan", "wangwu", "lisi"]
    //5、运算符
    let continueSortedNames = names.sorted(by: > )
    print(continueSortedNames)//结果:["zhaoliu", "zhangsan", "wangwu", "lisi"]
    //6、尾随闭包
    let otherNames = ["zhangsan","lisi","wangwu","zhaoliu"]
    let nextSortedNames = names.sorted{$0 > $1}
    print(nextSortedNames)//结果:["zhaoliu", "zhangsan", "wangwu", "lisi"]
  • 捕获值
  1. 一个闭包能够从上下文捕获已被定义的常量和变量。即使定义这些常量的原作用域已经不存在,闭包仍能够在其函数体内引用和修改这些值。
  2. 作为一种优化,如果一个值没有改变或者在闭包的外面,swift可能会使用这个值的拷贝而不是捕获。
  3. swift也处理来变量的内存管理操作,当变量不再需要时会被释放。
  4. 如果你建立来第二个,它将会有一个新的,独立的变量引用。
  • 闭包是引用类型
  1. 在swift中,函数和闭包都是引用类型。
  2. 无论你什么时候赋值一个函数或者闭包给常量或者变量,你实际上都是将常量和变量设置为对函数和闭包的引用。
  3. 如果你分配来一个闭包给类的实例的属性,并且闭包通过引用该实例或者它的成员来捕获实例,你将在闭包和实例键产生循环引用。
  • 【代码演示】
    //7、捕获值func makeIncerementer(forIncerement amount:Int) -> () ->Int {var runingTotal = 0func incrementer() ->Int{runingTotal += amountreturn runingTotal}return incrementer
    }let incrementByTen = makeIncerementer(forIncerement: 10)
    incrementByTen()//结果:10
    incrementByTen()//结果:20
    incrementByTen()//结果:30
    let incrementBySeven = makeIncerementer(forIncerement: 7)
    incrementBySeven()//结果:7
    incrementByTen()//结果:40
    let anotherIncrementBySeven = incrementByTen
    anotherIncrementBySeven()//结果:50
  • 逃逸闭包
  1. 当闭包作为一个实际参数传给函数的时候,并且它会在函数返回之后调用,我们就说这个闭包逃逸了。当你声明一个接受闭包作为形式参数的函数时候,你可以在形式参数前写@escaping来明确闭包是允许逃逸的。
  2. 闭包可以逃逸的一种方法是被存储在定义于函数外的变量。比如说,很多函数接受闭包实际参数来作为启动异步任务的回调。函数在启动之后任务返回,但是闭包要直到任务完成一一闭包需要逃逸。以便于稍后调用。
  3. 让闭包@escaping意味着你必须在闭包中显式引用self。
  • 自动闭包
  1. 自动闭包是一种自动创建的用来把作为实际参数传递给函数的表达式打包的闭包。它不接受任何实际参数,并且当它被调用时,它会返回内部打包表达式的值。
  2. 这个语法的好处在于通过写普通表达式代替显式闭包而使你省略包围函数形式参数的括号。
  3. 自动闭包允许你延迟处理,因此闭内部的代码直到你调用他的时候才会运行。对于有副作用或者占用资源的代码来说很有用。因为它可以允许你控制代码何时才进行求值。
  4. 当你传一个闭包作为实际参数到函数的时候,你会得到与延迟处理相同的行为。
  5. 通过@autoclousure标志标记它的形式参数使用了自动闭包。现在你可以调用函数就像接收了一个string实际参数而不是闭包。实际参数自动地转换为闭包,因为cunstomeProvider形式参数的类型被标记为@autoclosure标记。
  6. 如果你想要自动标允许逃逸,就同时使用@autoclosure和@escaping标志。
  • 【代码演示】
    //8、延迟处理
    var nameArray = ["zhangsan","lisi","wangwu"]
    let customerProvider = {nameArray.remove(at: 0)}
    print(nameArray.count)//结果:3
    print(customerProvider())//结果:zhangsan
    print(nameArray.count)//结果:2//9、逃逸闭包
    var providers:[() -> String] = []
    func collectCustomProviders(provider:@escaping () -> String){providers.append(provider)
    }//10、逃逸闭包加自动闭包
    func collectSubCustomProviders(provider:@autoclosure  @escaping () -> String){providers.append(provider)
    }collectSubCustomProviders(provider: nameArray.remove(at: 0))
    collectSubCustomProviders(provider: nameArray.remove(at: 0))
    print(nameArray.count)for provider in providers{
    print(provider())//结果:lisi wangwu
    }
    print(nameArray.count)//结果:0
    //10、引用self
    class SomeClass {var x:String = "hello"func doSometring(){collectSubCustomProviders(provider: self.x)}
    }