Swift中的泛型

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • Swift泛型介绍 泛型是为Swift编程灵活性的一种语法,在函数、枚举、结构体、类中都得到充分的应用,它的引入可...
    Bobby0322阅读 14,141评论 0 26
  • 什么时候需要使用泛型 在讲到泛型之前,先写一段代码(文中的代码都是Swift书写)。 这是一个很常见的也很简单的I...
    BennyLoo阅读 3,057评论 1 4
  • 作者:Thomas Hanning,原文链接,原文日期:2015/09/09译者:pmst;校对:numbbbbb...
    梁杰_numbbbbb阅读 433评论 0 4
  • 本文源自于泊学文档,同时在下方添加了扩展的案例,为了方便团队成员翻阅,记录之。 面向对象的方式 在面向对象的世界里...
    AKyS佐毅阅读 819评论 0 3
  • 紅樓夢 · 種菊 携鋤秋圃自移來,籬畔庭前故故栽。 昨夜不期經雨活,今朝有喜帶霜開。 冷吟秋色詩千首,醉酹寒香酒一...
    墨影teresa阅读 552评论 4 12