时隔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