Swift泛型

前言

OC缺乏一个重要特性,不支持泛型。Swift拥有了这一特性,是灵活性的语法,在函数、结构体、类、枚举中都可以应用,相当于暂位符的作用,当类型暂时不确定,只有等到调用函数时才能确定具体类型的时候可以引入泛型。

简单理解泛型就是先占位,具体占位做什么,用的时候再说。Swift的Array和Dictionary类型都是泛型集

应用场景

简单模仿一个栈操作,主要实现出栈、入栈两个功能

// MARK: - 模仿栈
class TestStack {
    var valueArray: [Int] = []
    
    /// 压栈
    func push(value: Int) -> () {
        valueArray.append(value)
    }
    
    /// 出栈
    func pop() -> (Int?) {
        if let lastValue = valueArray.last {
            valueArray.removeLast()
            return lastValue
            
        }else {
            return nil
        }
    }
}

这样实现的栈只能操作Int类型,如果需求变更,要处理String类型,怎么解决?替换所有Int为String可以解决,但这种代码明显上不了台面。此外另外我们还会想到Any类型,如下

// MARK: - 模仿栈
class TestStack {
    var valueArray: [Any] = []
    
    /// 压栈
    func push(value: Any) -> () {
        valueArray.append(value)
    }
    
    /// 出栈
    func pop() -> (Any?) {
        if let lastValue = valueArray.last {
            valueArray.removeLast()
            return lastValue
            
        }else {
            return nil
        }
    }
}

如此TestStack可操作类型就不局限于一种了,但是带来了两个问题:1、数据类型安全问题;2、每次对栈进行操作时,都需要进行一系列繁琐的类型转换(casting操作,使用as来进行类型转换)

这里简单介绍下Any和AnyObject的区别:

在 Swift 3 之前,我们可以写完一个项目都只用 AnyObject 来代表大多数实例,好像不用与 Any 类型打交道。但事实上,Any 和 AnyObject 是有明显区别的,因为 Any 可以代表 struct、class、func 等等几乎所有类型,而 AnyObject 只能代表 class 生成的实例。

那为什么之前我们在 Swift 2 里可以用 [AnyObject] 声明数组,并且在里面放 Int、String 等 struct 类型呢?这是因为 Swift 2 中,会针对这些 Int、String 等 struct 进行一个 Implicit Bridging Conversions,在 Array 里插入他们时,编译器会自动将其 bridge 到 Objective-C 的 NSNumber、NSString 等类型,这就是为什么我们声明的 [AnyObject] 里可以放 struct 的原因。

但在 Swift 3 当中,为了达成一个门真正的跨平台语言,相关提案将 Implicit Bridging Conversions 给去掉了。所以如果你要把 String 这个 struct 放进一个 [AnyObject] 里,一定要 as NSString,这些转换都需要显示的进行了——毕竟 Linux 平台默认没有 Objective-C runtime。这样各平台的表现更加一致。当然这是其中一个目标。

参照泛型的特性,定义一个泛型类型。使用泛型后的示例代码如下:
`
// MARK: - 模仿栈
class TestStack<T> {
var valueArray: [T] = []

/// 压栈
func push(value: T) -> () {
    valueArray.append(value)
}

/// 出栈
func pop() -> (T?) {
    if let lastValue = valueArray.last {
        valueArray.removeLast()
        return lastValue
        
    }else {
        return nil
    }
}

}
`

使用泛型:在初始化时通过明确的类型(这里用Int示例)来定义参数,之后编译器会将所有的泛型(T)替换成Int类型(如此实现的栈,最大优势在于能够匹配任何类型)。

       // 示例1:Int
        let myStack1 = TestStack<Int>()
        myStack1.push(value: 1)
        myStack1.push(value: 2)
        myStack1.push(value: 3)
        print(myStack1.pop() ?? "0")
        
        // 示例2:String
        let myStack2 = TestStack<String>()
        myStack2.push(value: "a")
        myStack2.push(value: "b")
        myStack2.push(value: "c")
        print(myStack2.pop() ?? "0")

泛型约束

泛型约束大致分为以下几种:

协议约束,泛型类型必须遵循某些协议
继承约束,泛型类型必须是某个类的子类类型
条件约束,泛型类型必须满足某种条件

协议约束

还拿TestStack栈说事儿,这里添加一个函数isContainValue,要判断栈中是否包含传入的元素,需要用到等式符(==)和不等符(!=)对任何两个该类型进行比较。Swift标准库中定义了一个Equatable协议,只有支持了这个协议才可以使用等式符(==)和不等符(!=),否则报红。所有的Swift标准类型自动支持Equatable协议,但是泛型由于不确定类型,系统不知对象是否支持Equatable协议,所以直接使用==或!=报红


泛型协议约束.png

解决方案,让TestStack栈中,让泛型T遵循Equatable协议


泛型协议约束1.png
继承约束

这里定义了三个类Person、Leader、Coder,其中Leader是继承Person,

// MARK: - 人
class Person: NSObject {
    func watchTV() -> () {
        print("Person看电视:人民的名义")
    }
}


// MARK: - 领导
class Leader: Person {
    override func watchTV() -> () {
        print("Leader看电视:人民的名义")
    }
}


// MARK: - 程序员
class Coder {
    func watchTV() -> () {
        print("coder看电视:人民的名义")
    }
}
泛型继承约束1.png
    func testWatchTV<T: Person>(obj:T) -> () {
        obj.watchTV()
    }

testWatchTV函数,接受一个泛型参数,要求该泛型类型必须继承Person,否则爆红。

条件约束

在类型名后面使用where来指定对类型的特殊需求,比如限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类等

// 定义一个协议
@objc protocol TestProtocol {
    @objc optional func testOne() -> ()
}

// MARK: - 人
class Person: NSObject, TestProtocol {
    var name: String?
    
    func watchTV() -> () {
        print("Person看电视:人民的名义")
    }
}


// MARK: - 领导
class Leader: NSObject {
    var name: String?
    
    func watchTV() -> () {
        print("Leader看电视:人民的名义")
    }
}
泛型条件约束1.png

testCondition函数要求传入的参数都是P类型的继承自Person,L类型继承自Leader,同时还附加了条件(where)这两个类都遵守了TestProtocol协议,由于Leader类型没有遵守TestProtocol协议,条件不满足所以爆红。修改方法则是Leader类遵守TestProtocol。

参考链接:http://www.jianshu.com/p/a907f0c09a60
参考链接:http://swift.gg/2015/09/16/swift-generics/

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

推荐阅读更多精彩内容

  • 泛型代码可以确保你写出灵活的,可重用的函数和定义出任何你所确定好的需求的类型。你的可以写出避免重复的代码,并且用一...
    iOS_Developer阅读 787评论 0 0
  • 原文:Generics Manifesto -- Douglas Gregor 译者注 在我慢慢地深入使用 Swi...
    kemchenj阅读 1,899评论 0 6
  • Swift 提供了泛型让你写出灵活且可重用的函数和类型。Swift 标准库是通过泛型代码构建出来的。Swift 的...
    零度_不结冰阅读 416评论 0 0
  • 本文转载自http://blog.csdn.net/youshaoduo/article/details/5486...
    desunire阅读 1,928评论 0 0
  • 附上Apple文档翻译版 1.为什么需要泛型的使用 其实说白了很简单。就是对于方法结构相同,但是由于类型不同,而需...
    mdiep阅读 316评论 0 4