Swift学习笔记第五篇(闭包和枚举)

闭包

闭包:自包含的代码块,可以在代码中被传递和使用,闭包可以捕获和存储其所在上下文任意常量和变量的引用 这就是所谓的闭包并包裹着这些常量和变量,俗称闭包

闭包三种形式
1.全局函数是一个有名字但不会捕获任何值得闭包
2.嵌套函数是一个有名字并且可以捕获其封闭函数内值得闭包
3.闭包表达式是一个利用轻量级语法所写的可以捕获其上下文的变量和常量的匿名闭包

sorted函数为例

public func sorted(by areInIncreasingOrder: (Element, Element) -> Bool) -> [Element]

func someFunction(externalParameterName localParameterName: Int) {
function body goes here, and can use localParameterName
to refer to the argument value for that parameter
}
// 如果你希望函数的使用者在调用函数时提供参数名字,那就需要给每个参数除了局部参数名外再定义一个外部参数名。外部参数名写在局部参数名之前,用空格分隔。

1.可以看到,by其实就是暴露给外部的参数名,而areInIncreasingOrder是内部变量名
2.参数:闭包函数 该闭包函数需要传入与数组类型相同的两个值,并返回一个布尔类型值来告诉sorted函数当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回true,反之返回false
3.看下最普通的调用

let names = ["aaaa","ccc","fff","ggggg","tttt","hhhh"]

func sortClosureFunc(s1:String,s2:String)->Bool{
    return s1 > s2
}

var results = names.sorted(by: sortClosureFunc)

闭包语法和使用

{
    (parameters) -> returenType in
        statement
}
上面的代码可以简化为如下
results = names.sorted(by: { (s1:String, s2:String) -> Bool in
    return s1 > s2
})
闭包的函数体部分由关键字in引入。 该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。因此一行搞定
results = names.sorted(by: { (s1:String, s2:String) -> Bool in return s1 > s2})

闭包语法之根据上下文推断

// 实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数时,都可以推断出闭包的参数和返回值类型,这意味着您几乎不需要利用完整格式构造任何内联闭包
results = names.sorted(by: { s1,s2 in
    return s1 > s2
})

闭包语法之隐藏return

// 单表达式可以通过隐藏return关键字来隐藏单行返回的结果
results = names.sorted(by: {s1,s2 in s1 > s2})
print(results)

参数名称缩写

Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数。
results = names.sorted(by: {$0 < $1})
print(results)

运算符函数

实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。 Swift 的String类型定义了关于大于号 (>) 的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值
results = names.sorted(by: >)
print(results)

尾随闭包(系统默认)

如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性
如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉

// 尾随闭包 Closure Training
func someFunctionClosure(closure:()->()){
    // 函数体
}

// 老式写法
someFunctionClosure(closure: {})

// 尾随写法
someFunctionClosure(){}


// 如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉。  推荐写法
someFunctionClosure {

}
因此上面的Demo可以简化成
results = names.sorted{$0<$1}
print(results)

枚举

普通写法

为了理解下面的Demo 简单介绍下枚举
语法
enum SomeEnumeration {
  // enumeration definition goes here
}
定义一个枚举
enum CompassPoint {
  case North
  case South
  case East
  case West
}

访问赋值
var directionToHead = CompassPoint.West

已经确认directionToHead类型之后也可以这样范文
directionToHead = .East

特有的写法

// 商品条形码 枚举
enum Barcode {
  case UPCA(Int, Int, Int)
  case QRCode(String)
}
// “定义一个名为Barcode的枚举类型,它可以是UPCA的一个相关值(Int,Int,Int),或者QRCode的一个字符串类型(String)相关值。”

// 创建和赋值
var productBarcode = Barcode.UPCA(8, 85909_51226, 3)
productBarcode = .QRCode("ABCDEFGHIJKLMNOP")

// 条件筛选打印
// 你可以在switch的 case 分支代码中提取每个相关值作为一个常量(用let前缀)或者作为一个变量(用var前缀)来使用
switch productBarcode {
case .UPCA(let numberSystem, let identifier, let check):
    println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
case .QRCode(let productCode):
    println("QR code with value of \(productCode).")
}
// 输出 "QR code with value of ABCDEFGHIJKLMNOP.”

// 如果一个枚举成员的所有相关值被提取为常量,或者它们全部被提取为变量,为了简洁,你可以只放置一个var或者let标注在成员名称前
switch productBarcode {
case let .UPCA(numberSystem, identifier, check):
    println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
case let .QRCode(productCode):
    println("QR code with value of \(productCode).")
}
// 输出 "QR code with value of ABCDEFGHIJKLMNOP."

枚举默认值和可选绑定判断

// 定义
enum Planet: Int {
    case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
// 根据默认值推断
let earthsOrder = Planet.Earth.rawValue
// earthsOrder is 3

// 可选绑定 ? or !
let positionToFind = 9
if let somePlanet = Planet(rawValue: positionToFind) {
    switch somePlanet {
    case .Earth:
        println("Mostly harmless")
    default:
        println("Not a safe place for humans")
    }
} else {
    println("There isn't a planet at position \(positionToFind)")
}
// 输出 "There isn't a planet at position 9

// 第一个if根据后面的表达式传入的是9,无法找到,因此返回的是nil,无法赋值给let常量,因此进入else分支,这也是可选值判断的一个常用方法

闭包示例—官方文档的Demo

// 官方文档的闭包例子
enum HTTPResponse {
    case ok
    case error(Int)
}

let responses: [HTTPResponse] = [.error(500), .ok, .ok, .error(404), .error(403)]
let sortedResponses = responses.sorted {
    switch ($0, $1) {
    // Order errors by code
    case let (.error(aCode), .error(bCode)):
        return aCode < bCode

    // All successes are equivalent, so none is before any other
    case (.ok, .ok): return false

    // Order errors before successes
    case (.error, .ok): return true
    case (.ok, .error): return false
    }
}
print(sortedResponses)
//      Prints "[.error(403), .error(404), .error(500), .ok, .ok]"
[__lldb_expr_417.HTTPResponse.error(403), __lldb_expr_417.HTTPResponse.error(404), __lldb_expr_417.HTTPResponse.error(500), __lldb_expr_417.HTTPResponse.ok, __lldb_expr_417.HTTPResponse.ok]

闭包示例—map函数

// map numbers.map(<#T##transform: (Int) throws -> T##(Int) throws -> T#>)
// 使用 map 来遍历集合并对集合中每一个元素进行同样的操作
// 基本上就是默认尾随闭包,参数只有闭包,省略(),省略return 单行表达式闭包可以通过隐藏return关键字来隐式返回单行表达式的结果

let digistName = [0:"zore",1:"one",2:"two",3:"three",4:"four",5:"five",6:"six",7:"seven",8:"eight",9:"nine"]
let numbers = [213,44,658]

let stringNumbers = numbers.map { (num:Int) -> String in
    var num1 = num
    var string = ""
    while num1 > 0{
        string = digistName[num1 % 10]! + string
        num1 = num1 / 10
    }
    return string
}
print(stringNumbers)
// ["twoonethree", "fourfour", "sixfiveeight"]

let stringNumber2 = numbers.map {NumberFormatter.localizedString(from: NSNumber.init(value: $0), number: .spellOut) }
print(stringNumber2)
// ["two hundred thirteen", "forty-four", "six hundred fifty-eight"]

捕获上下文变量

闭包可以在其定义的上下文中捕获常量或变量。 即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。

Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。 嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量

func makeIncreaseFunc(extensionNumber localNumber: Int) -> ()->Int{
    var totalAmount = 0;
    func increaseMent()->Int{
        totalAmount += localNumber
        return totalAmount
    }
    return increaseMent
}

let inc = makeIncreaseFunc(extensionNumber: 20)
inc() // 20
inc() // 40

let inc2 = makeIncreaseFunc(extensionNumber: 50)
inc2() // 50
inc2() // 100
makeIncreaseFunc 函数里面嵌套了 increaseMent,嵌套函数increaseMent从上下文捕获了两个值 totalAmount 和 localNumber 之后 makeIncreaseFunc把 increaseMent作为返回值返回,每次外部调用 返回值的时候,就会返回totalAmount的值

我们将函数赋给变量,这样我们可以通过变量来调用函数。运行结果使得我们可以发现,每次调用makeIncreaseFunc其实是创建了一个新的对象,inc1,和inc2并不一样。它们有着自己的值,之间并不共享。这说明,这些函数是一等函数,它们是对象,可以有多个实例,可以被赋给变量,感觉像是一个析构函数,内部会创建对应的对象,然后对象会引用外部函数的一些值,从而达到局部变量常驻内存的情况

/*
func increaseMent()->Int{
    totalAmount += localNumber
    return totalAmount
}
*/
// 单独看这个函数,没有传任何参数,而是通过值捕获和increaseMent一样保存在的内存中  捕获是强引用,保证makeIncreaseFunc挂了的时候,其参数和内部参数都能继续使用

对比下简单的两个例子

1.函数嵌套,没有返回函数

// 如果单纯的函数嵌套,局部变量使用完之后就会被回收,再次调用函数的时候就会是原先的数据
func add(num:Int)->Int
{
    var value = 100
    func addDouble()->Int{
        value += num
        return value
    }
    return addDouble()
}
let testFun = add
testFun(200) // 300
testFun(200) // 300
testFun(200) // 300

2.函数嵌套,返回内部函数,实现闭包

// 内部嵌套函数通过返回值返回,外部进行引用,让局部变量常驻内存,这才算是个闭包
func add2(num:Int)->()->Int
{
    var value = 100

    func addDouble()->Int{
        value += num
        return value
    }
    return addDouble
}

let testFunc2 = add2(num: 200)
testFunc2() // 300
testFunc2() // 500
testFunc2() // 700

个人总结

1.在说闭包之前,需要先清楚“自由变量”的概念。在某个作用域中,如果使用未在本作用域中声明的变量,对于此作用域来说,该变量就是一个自由变量。
2.闭包,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。另一种说法认为闭包并不是函数,而是由函数和与其相关的引用环境组合而成的实体。这是因为,闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。而函数只会有一个实例。我感觉如果把闭包和block一样当做一个对象,这就很好理解了,你的值被对象引用了,那么这个对象未销毁之前,无论之前创造这些变量的对象是否有销毁,只有有强指针,这些变量都还能被使用
3.闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
4.特性:函数嵌套函数,内部函数可以引用其外部变量和参数,能让局部变量保活
5.好处:可以是局部变量常驻内存,自由自支配释放,避免全局变量的污染,私有成员的存在

你可能感兴趣的:(Swift学习笔记)