Swift 泛型和闭包 | 青训营笔记

299 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第14天

泛型

泛型是为所存储或使用的一个或多个类型具有占位符(类型形参)的类、结构、接口和方法

泛型集合类可以将类型形参用作其存储的对象类型的占位符;类型形参呈现为其字段的类型和其方法的参数类型

泛型方法可将其类型形参用作其返回值的类型或用作其形参之一的类型

// 泛型版本 
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

对类使用泛型,占位符写在类名后的 <>,泛型类型一般用 Element 表示,用一个叫做 Element 的类型形式参数代替了实际的 String 类型

class RandomSelectStudent<Element> {
    var lastRandom = 42.0
    let m = 139968.0
    let a = 3877.0
    let c = 29573.0
    // students是一个Element数组
    var students: [Element] = []
    func random() -> Double {
        //lastRandom*a+c再对m取余
        lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy:m))
        //取余之后再除以m必定是一个0-1的数字
        return lastRandom / m
    }     
    // 返回一个Element类型变量
    func randomSelectStudent() -> Element {
        let count = students.count
        // 类型转换
        let index = Int(random() * Double(count)) + 1
        return students[index]
    }
}

// 调用
let generator = RandomSelectStudent<String>()

闭包

基本概念

闭包是可以在你的代码中被传递和引用的功能性独立代码块。Swift 中的闭包和 C 以及 Objective-C 中的 blocks 很像,还有其他语言中的匿名函数也类似

但是 Swift 相对于其他语法,Swift 的闭包表达式拥有简洁的风格,鼓励在常见场景中实现简洁,无累赘的语法。常见的优化包括:

  • 利用上下文推断形式参数和返回值的类型
  • 单表达式的闭包可以隐式返回
  • 简写实际参数名
  • 尾随闭包语法

Swfit的闭包格式为

{ (parameters) -> (return type) in
    statements
}

下面这个例子展示一个sorted闭包表达版本,这也是我们本轮重点要优化的函数sorted(by:)

let studentsSorted = students.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})

利用上下文推断形式参数和返回值的类型

由于排序闭包为实际参数来传递给方法,Swift 就能推断它的形式参数类型和返回类型。 sorted(by:) 方法是在字符串数组上调用的,所以它的形式参数必须是一个 (String, String) -> Bool 型的函数。这意味着 (String, String)Bool 类型不需要写成闭包表达式定义中的一部分。因为所有的类型都能被推断,返回箭头 ( ->) 和围绕在形式参数名周围的括号也能被省略

let studentsSorted = students.sorted(by: { (s1, s2) -> Bool in
    return s1 > s2
})

单表达式闭包

单表达式闭包能够通过从它们的声明中删掉 return 关键字来隐式返回它们单个表达式的结果

let studentsSorted = students.sorted(by: { s1, s2 in s1 > s2 })

Swift 的 String 类型定义了关于大于号 > 的特定字符串实现,让其作为一个有两个 String 类型形式参数的函数并返回一个 Bool 类型的值。这正好与 sorted(by:) 方法的第二个形式参数需要的函数相匹配。因此,你能简单地传递一个大于号,并且 Swift 将推断你想使用大于号特殊字符串函数实现:

let studentsSorted = students.sorted(by: >)

尾随闭包语法

如果你需要将一个很长的闭包表达式作为函数最后一个实际参数传递给函数且闭包表达式很长,使用尾随闭包将增强函数的可读性。尾随闭包是一个被书写在函数形式参数的括号外面(后面)的闭包表达式,但它仍然是这个函数的实际参数。当你使用尾随闭包表达式时,不需要把第一个尾随闭包写对应的实际参数标签。函数调用可包含多个尾随闭包,但这里我们暂时只学习单一尾随闭包的写法:

下面这个例子,是一个函数 someFunctionThatTakesAClosure,然后他参数为一个 closure 闭包,返回 Void 值,然后下面是正常闭包和尾随闭包的写法

func someFunctionThatTakesAClosure(_ closure:() -> Void) {
   //function body goes here
}

// 正常闭包写法
someFunctionThatTakesAClosure({
    // closure body
})

// 尾随闭包
someFunctionThatTakesAClosure() {
    // closure body
}