Swift泛型笔记

泛型可以让代码处理类型更加灵活,在某些场景下可以很大程度的重用代码,泛型是什么,使用泛型的好处,这里不多说,网上有很多文章介绍的很详细,这里只讨论用法,Swift的泛型与其他语言有些类型,又有些不一样,搜了一下发现,网上的文章只描述了一点,并不全面,看完后依然没能很全面的说明泛型的用法,在这里记录完整的用法

定义泛型

swift的泛型定义方式有两种,一种通过<T>指定泛型参数,而在属性或方法,使用指定的泛型类型,下面是一个泛型方法的声明

泛型方法

/// 交换两个同类型的变量
func swap<T>(a: inout T, b: inout T) {
    let c = a
    a = b
    b = c
}
var (a, b) = (2, 1)

swap(&a, &b)
print(a, b)         // 1, 2

swap(&a, &b)        
print(a, b)         // 2, 1

泛型类

上面是泛型方法的定义,如果在类里面,可以对类声明泛型

class Test<T> {
    /// 泛型属性
    var testObj: T?

    /// 泛型方法,可以直接使用类的泛型参数T
    func swap<T>(a: inout T, b: inout T) {
        let c = a
        a = b
        b = c
    }

    /// 方法的泛型定义可以不依赖于类的泛型参数
    func swap2<TK>(a: inout TK, b: inout TK) {
        let c = a
        a = b
        b = c
    }
}

// 属性
let t = Test<Int>()
t.testObj = 12
print(t.testObj)        // 12

var a: Int? = 2
var b: Int? = 1
t.swap(a: &a, b: &b)
print(a, b)            // 1, 2

var s1: String? = "a"
var s2: String? = "b"
t.swap2(a: &s1, b: &s2)
print(s1, s2)            // b, a

泛型参数

泛型可以支持多个类型参数,在声明处用逗号分开

class Test {
    /// 多个泛型参数
    func test<T1, T2>(t1: T1, t2: T2) -> Int {
        print("t1: \(t1), t2: \(t2)")
        return 1
    }
}

let t = Test()
let result = t.test(t1: NSObject(), t2: "abc")

泛型约束

类型约束定义在泛型声明处,支持类约束和protocol约束,如下

protocol Run {
    func run()
}

protocol Fly {
    func fly()
}

/// 单协议约束
func test<T: Run>(animal: T) {
    animal.run()
}

/// 类型和多协议约束
func test<T: NSObject & Fly & Run>(bird: T) {
    bird.fly()
    bird.run()
}

泛型可以用在方法参数,方法返回值,属性上

协议泛型

除了类,结构体和枚举也支持泛型,用法与类一样,但是协议protocol不能像上面一样使用
协议只能通过关联类型associatedtype来实现泛型的功能,相当于泛型声明为协议的成员,而泛型的成员在实现的时候指定

protocol Write {
    /// 关联类型Element,相当于上面的T
    associatedtype Element
    func write(_ element: Element)
}

class File: Write {
    // 在实现关联类型的协议的时候,需要指定关联类型
    typealias Element = String

    // 实现协议方法
    func write(_ element: File.Element) {
        print(element)
    }
}

关联类型的约束

关联类型的约束与普通泛型约束一样,在声明的地方后面添加,通过逗号隔开

 protocol Write {
    associatedtype Element: NSObject, Encodable
    func write(_ element: Element)
}

关联自身类型

protocol利用associatedtype关联自身类型

protocol Equalable {
    func equal(_ a: Self) -> Bool
}

class Test: Equalable {
    var id: Int = 0
    
    init(id: Int) {
        self.id = id
    }
    
    func equal(_ a: Test) -> Bool {
        return self.id == a.id
    }
}

let a = Test(id: 1)
let b = Test(id: 2)
a.equal(b)              // false

associatedtype冲突

associatedtype有另一个问题没有解决,就是类型冲突,如下

protocol Read {
    associatedtype Element
    func read() -> Element
}
protocol Write {
    associatedtype Element
    func write(a: Element)
}

// 两个Element名字相同,无法指定
class ReadWrite: Read, Write {
    func read() -> Int {
        return 5
    }
    func write(a: String) {
        print("writing \(a)")
    }
}

在stackoverflow上有个不完美的解决方案:
https://stackoverflow.com/questions/37736457/protocol-with-same-associated-type-name

protocol ReadInt: Read {
    associatedtype Element = Int
}
protocol WriteString: Write {
    associatedtype Element = String
}

class ReadWrite: ReadInt, WriteString {
    func read() -> Int {
        return 5
    }
    func write(a: String) {
        print("writing \(a)")
    }
}

关于associatedtype

对于其他语言大多都使用直接指定类型<T>的方式声明泛型,而swift为什么还要搞出一个associatedtype呢,这篇文章有分析:http://www.cocoachina.com/swift/20160726/17188.html

使用关联类型,类似于类型当成成员属性使用,可以解耦依赖,如果需要使用对象的关联的类型,而类型与当前对象并没有直接关系,则通过成员的关联类型引用可以避免直接依赖对象类型,如下例子:机动车(Automobile)、燃料(Fuel)、尾气(Exhaust)

/// 机动车
public protocol Automobile {
    associatedtype FuelType
    associatedtype ExhaustType
    func drive(fuel: FuelType) -> ExhaustType
}

/// 燃料
public protocol Fuel {
    associatedtype ExhaustType
    func consume() -> ExhaustType
}

/// 尾气
public protocol Exhaust {  
    func emit()
}

实现

public struct UnleadedGasoline: Fuel {
    public func consume() -> E {    
        print("consuming unleaded gas...")  
        return E()
    }
}

public struct CleanExhaust: Exhaust {
    public func emit() {  
        print("this is some clean exhaust...")
    }
}

public class Car: Automobile {
    public func drive(fuel: F) -> E {    
        return fuel.consume()
    }
}

实际依赖关系:
机动车 -> 燃料
燃料 -> 尾气

但是代码有个问题是在定义Car的时候,必须声明尾气的类型E,而实际上机动车是不依赖于尾气的类型的,尾气的类型取决于燃料,如果Car支持两种燃料FuelA和FuelB,而两种燃料产生的尾气E1和E2,drive方法无法同时满足,只能定义两个方法

public class Car: Automobile {

    public func drive(fuel: F1) -> E1 {    
        return fuel.consume()
    }
    public func drive(fuel: F2) -> E2 {    
        return fuel.consume()
    }
}

如果使用关联类型的话可以解决这个问题,把尾气关联到燃料上

public class Car: Automobile {
    public func drive(fuel: F) -> F.ExhaustType {    
        return fuel.consume()
    }
}

以上就是Swift泛型的全部要点

blog原文: http://blog.bombox.org/2018-01-14/swift-generic/

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