iOS-Swiftswift开发技巧iOS Developer

Swift中的泛型

2016-07-08  本文已影响543人  MelodyZhy

时隔1个月,2个月,3个月。。。我终于回来了。。公司项目太忙了以至于简书一直没有更新。而且公司项目还是OC的。。最近忙的也差不多了,还得继续学咱们的Swift啊,那么今天呢,就写一篇我对Swift中泛型的理解吧。

泛型(generic)

泛型是Swift的强大特征之一,它可以让我们的代码更加灵活,让我们的函数可重用性更高,可以让我们的代码更利于维护,逻辑更加清晰。


泛型的使用场景

现在我们有一个这样的需求交换两个整数的值,那么我们的函数可能是这样写的

func swapInt(inout a: Int, inout b: Int) {
    let temp = a
    a = b
    b = temp
}
    var a = 10
    var b = 20
    // 函数调用
    swapInt(&a, b: &b)

这时候我们感觉万事大吉了,下班回家!产品一句话需求改了现在要交换两个String的值,沃嚓。。难到还得写个函数,这时候Swift中泛型的作用就有展现的地方了,只需稍作改动我们的函数就会变得非常强大。

func swapGeneric <T>(inout a: T, inout b: T) {
    let temp = a
    a = b
    b = temp
}
    var str = "Hello, playground"
    var str1 = "Hello, World"
    // 函数调用
    swapGeneric(&str, b: &str1)

那么我们来对比一下这两个函数

func swapInt(inout a: Int, inout b: Int)
func swapGeneric<T>(inout a: T, inout b: T)

我们可以容易的发现泛型函数无非就是使用了节点类型命名,通常我们用字母T来代替实际类型名(如Int,String,Float等),当有多种类型的时候可以用U,E或其他字符表示,这个没有强制要求,都是写习惯了自然就用那几个字母了。节点类型名并不是表示T必须是任何类型,而是表示规定a和b必须是同一个类型的T,只有函数在每一次调用的时候传入的实际类型才能决定T所代表的类型。


泛型约束

我们有一个判断两个参数是否相等的泛型函数,如下

// ❌
func isEquals<T>(a: T, b: T) -> Bool {
    return (a == b)
}

是不是你也觉得这样写是对的?这样写其实很明显是错误的,仔细想一下a和b有可比性嘛,很显然是没有的,所以我们需要这个泛型T遵守Comparable协议,也就是我们要给这个泛型T添加约束,只有符合这个约束的类型才能使用这个函数,改进的函数如下

// ✅
func isEquals<T: Comparable>(a: T, b: T) -> Bool { 
    return (a == b)
}

如果你使用Swift编写代码,却说自己没有用到过泛型,那只能说明😄。。许多Swift标准库是通过泛型代码构建出来的,最常见的例子:Swift的Array和Dictionary类型都是泛型集,你可以创建Int类型的数组,也可以创建String类型的,同样也可以创建存储任何指定类型的字典,而且这些类型可以是没有限制的。

更深入的理解泛型

上一篇简书我们介绍了Swift数组中Map,FlatMap,Filter,Reduce的使用,那么今天就来说一下Map以及Reduce的内部实现,从而更深入的理解Swift中的泛型。

MyMap

首先我们先看看使用Swift中原有map的实现,代码如下

let fruits = ["apple", "banana", "orange"]
// 将字符串转换成字符串的长度
func fruit(fruit: String) -> Int? {
    let length = fruit.characters.count
    guard length > 0 else {
        return nil
    }
    return length
}
// map接受一个定义规则的函数
let counts = fruits.map(fruit)
// [Optional(5), Optional(6), Optional(6)]
print(counts)

接下来我们实现一下自己的map,代码如下

extension Array {
    // 定义 BeforeType 为之前的类型 BecomeType 为最终转化成的类型
    // 这里我们需要接受一个transform闭包,来定义我们转化的规则
    func myMap<BeforeType, BecomeType>(transform:
        (BeforeType) -> (BecomeType)) -> [BecomeType] {
        // 定义一个转化后类型的空数组
        var output: [BecomeType] = []
        for item in self {
            // 通过闭包转化类型
            let transformed =
                transform(item as! BeforeType)
            // 添加到之前的创建的空数组
            output.append(transformed)
        }
        return output
    }
}

let fruits = ["apple", "banana", "orange"]
// 将字符串转换成字符串的长度
func fruit(fruit: String) -> Int? {
    let length = fruit.characters.count
    guard length > 0 else {
        return nil
    }
    return length
}
// map接受一个定义规则的函数
let counts = fruits.myMap(fruit)
// 我们可以看到返回的结果是一样的
// [Optional(5), Optional(6), Optional(6)]
print(counts)
// 由此可以看出map内部就是这样实现的

MyReduce

首先我们先看看使用Swift中原有reduce的实现,代码如下

var numArray = [1, 2, 3, 4, 5, 6]
// reduce有两个参数,第一个是用来表示第一次“合并”之前的初始值。因为我们要求和,所以它是0。
// 第二个参数combine是一个闭包,用来表示“合并”的规则。
// 它的第一个参数是“每一次合并前”的初始值,第二个参数是要“合并”进来的对象。
// 在我们的例子里,$0是0,$1表示数组中的每一个对象。
let sum = numArray.reduce(0, combine: { $0 + $1 })
// 21
print(sum)

接下来我们实现一下自己的reduce

extension Array {
    // 由于最终“合并”出来的结果有可能和参与合并的值类型不同
    //(例如:把一个整数数组拼接成一个字符串)
    // 所以myReduce需要两个泛型类型
    // 一个表示“合并”后的类型BecomeType
    // 一个表示参与“合并”的类型BeforeType
    // myReduce接受两个参数,initial用来表示整个合并前的初始值,它的类型是BecomeType
    // combine是一个闭包,用来表示合并的规则。
    // 每一次调用,都是把当前要合并的值合并到上一次的合并结果里,然后把新的合并结果返回
    func myReduce<BecomeType, BeforeType>(initial: BecomeType,
                  combine: (BecomeType, BeforeType) -> BecomeType) -> BecomeType {
        var seed = initial
        
        for item in self {
            seed = combine(seed, item as! BeforeType)
        }
        // 最后,当所有的合并都完成之后,myReduce返回最终的值
        return seed
    }
}

var numArray = [1, 2, 3, 4, 5, 6]
let sum = numArray.myReduce(0, combine: { $0 + $1 })
// 21
print(sum)

好了,Swift中泛型的介绍就到这了,接下来我会写一些有关POP和Reactive Programming方面的介绍,欢迎大家持续关注我,这次我不会脱更那么久了哦~
点击关注---->MelodyZhy

上一篇下一篇

猜你喜欢

热点阅读