泛型

泛型代码可以让你写出根据自我需求定义、适用于任何类型的,灵活且可重用的函数和类型。它的可以让你避免重复的代码,用一种清晰和抽象的方式来表达代码的意图。

泛型所解决的问题

先来看一个交换两个int值的例子:

var number1 = 5
var number2 = 10 func swapTwoInts(inout a: Int, inout _ b: Int) {
let temp = a
a = b
b = temp
} swapTwoInts(&number1, &number2)

因为Int是值类型,必须使用inout关键字(输入输出参数)来保证两个值的交换,但是这个方法只能交换两个Int值,如果想要实现两个Double,String就不得不声明另两个方法,但是两个方法内部实现会与这个方法内部相同,况且再生成两个方法或多个会很耗时。所以我们用泛型来解决这个问题。

泛型函数

var number1 = 5
var number2 = 10 var string1 = "string1"
var string2 = "string2" var double1 = 3.5
var double2 = 18.3 func swapTwoValues<T>(inout a: T, inout _ b: T) {
let temp = a
a = b
b = temp
} swapTwoValues(&number1, &number2)
swapTwoValues(&string1, &string2)
swapTwoValues(&double1, &double2)

这是泛型函数,适用于任何类型,这样可以解决重复代码的问题。写法是:func 函数名<泛型名>(参数名:泛型名) { 函数体 }

注意:swapTwoValues方法是模仿Swift标准库中的swap函数,如果以后想要实现两个值交换,可以使用swap函数。

多个泛型名:

let dic = ["key1" : 33, "key2" : 44.8, "key3" : "Name"]

func printDictionary<Key, Value>(key key: Key, value: Value) {
// 命名泛型时遵循驼峰式命名
print("键是\(key), 值是\(value)")
} for (key, value) in dic {
printDictionary(key: key, value: value)
}
//键是key1, 值是33
//键是key3, 值是Name
//键是key2, 值是44.8

泛型类型

先来看一个非泛型的类型,一个Int型的栈实例:

struct IntStack {
var items = [Int]() mutating func push(item: Int) {
items.append(item)
} mutating func pop() -> Int {
return items.removeLast()
}
} var stack = IntStack(items: [11, 5, 38])
let last = stack.pop()
print(last) // 38 stack.push(150)

可以看到上面的例子是一个栈的结构(先进后出),但是IntStack类型内的属性和接口只适用于Int,下面来实现一个泛型的栈:

struct Stack<T> {
var items = [T]() mutating func push(item: T) {
items.append(item)
} mutating func pop() -> T? {
if items.count == 0 {
return nil
}
return items.removeLast()
} func discretion() -> String {
return "这是一个泛型的栈结构类型,可以适用于所有类型"
}
} var intStack = Stack(items: [11, 5, 38])
let lastInt = intStack.pop()
print(lastInt) // 38
intStack.push(150)
print(intStack.items) // [11, 5, 150] var stringStack = Stack(items: ["Some String", "Alex"])
let lastString = stringStack.pop()
print(lastString) // Alex
stringStack.push("Alisa")
print(stringStack.items) // ["Some String", "Alisa"]

泛型类型可以理解为:泛型T定义了一个"某种类型T"、以便在类型内部用,当某种类型T被赋予一个类型后(如Int或String)不可更改为其他类型。

写法是:关键字(struct, class, enum) 类名<泛型> : 父类,或遵循的协议

扩展一个泛型类型

当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。更加方便的是,原始类型定义中声明的类型参数列表在扩展里是可以使用的,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。

struct Stack<T> {
var items = [T]() mutating func push(item: T) {
items.append(item)
} mutating func pop() -> T? {
if items.count == 0 {
return nil
}
return items.removeLast()
} func discretion() -> String {
return "这是一个泛型的栈结构类型,可以适用于所有类型"
}
} extension Stack {
// 一个计算属性,返回栈顶的item
var topItem: T? {
return items.isEmpty ? nil : items.last
}
}

类型约束

有的时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定类型是非常有用的。类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。

例如,Swift 的Dictionary类型对作用于其键的类型做了些限制。在字典的描述中,字典的键类型必须是可哈希,也就是说,必须有一种方法可以使其被唯一的表示。Dictionary之所以需要其键是可哈希是为了以便于其检查其是否已经包含某个特定键的值。如无此需求,Dictionary既不会告诉是否插入或者替换了某个特定键的值,也不能查找到已经存储在字典里面的给定键值。

这个需求强制加上一个类型约束作用于Dictionary的键上,当然其键类型必须遵循Hashable协议(Swift 标准库中定义的一个特定协议)。所有的 Swift 基本类型(如StringInt, Double和 Bool)默认都是可哈希。

约束语法:

func someFunction<T: SomeSuperClass, U: SomeProtocol>(someT: T, someU: U) -> someType {
// 函数体
}

约束实例:

func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
for (index, value) in array.enumerate() {
if value == valueToFind {
// 因为这里用到了"=="运算符,所以在泛型T后面要增加约束:T必须遵循Equtable协议,这样才能使用"=="运算符。
return index
}
}
return nil
} print(findIndex([10,22,86], valueToFind: 86)) // 2

关联类型

当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型作为协议的一部分,给定了类型的一个占位名(或别名)。作用于关联类型上实际类型在协议被实现前是不需要指定的。关联类型被指定为typealias关键字。

非泛型类型例子:

protocol Container {
// 这是判断swift版本的方法
#if swift(>=2.2)
// 如果在2.2版本以上 用associatedtype关键字
associatedtype ItemType
#else
// 如果在2.2之下,还是用typealisa关键字
typealisa ItemType
#endif mutating func append(item: ItemType)
var count: Int { get }
subscript (i: Int) -> ItemType? { get }
} struct IntStack: Container {
var items = [Int]()
mutating func push(item: Int) {
items.append(item)
} mutating func pop() -> Int? {
if items.count == 0 {
return nil
}
return items.removeLast()
} // 遵循Container协议
typealias ItemType = Int
mutating func append(item: ItemType) {
self.push(item)
} var count: Int {
return items.count
} subscript (i: Int) -> ItemType? {
return items.isEmpty ? nil : items[i]
}
}

Container协议为了适配多个类型而声明了一个typealias Item(别名),后面的函数和属性都用到了这个别名,这个别名的具体类型需要遵循这个协议的类型来指定,如:typealias ItemType = Int。

泛型类型的例子:

protocol Container {
// 这是判断swift版本的方法
#if swift(>=2.2)
// 如果在2.2版本以上 用associatedtype关键字
associatedtype ItemType
#else
// 如果在2.2之下,还是用typealisa关键字
typealisa ItemType
#endif mutating func append(item: ItemType)
var count: Int { get }
subscript (i: Int) -> ItemType? { get }
} struct Stack<T>: Container {
var items = [T]() mutating func push(item: T) {
items.append(item)
} mutating func pop() -> T? {
if items.count == 0 {
return nil
}
return items.removeLast()
} // 遵循Container协议
typealias ItemType = T
mutating func append(item: ItemType) {
self.push(item)
} var count: Int {
return items.count
} subscript (i: Int) -> ItemType? {
return items.isEmpty ? nil : items[i]
}
}

Where语句

对关联类型定义约束是非常有用的。你可以在参数列表中通过where语句定义参数的约束。一个where语句能够使一个关联类型遵循一个特定的协议,以及(或)那个特定的类型参数和关联类型可以是相同的。你可以写一个where语句,紧跟在在类型参数列表后面,where语句后跟一个或者多个针对关联类型的约束,以及(或)一个或多个类型和关联类型间的等价关系。

func allItemMatch<C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>(someContainer: C1, anotherContainer: C2) -> Bool {
// 检查两个Container的元素个数是否相同
if someContainer.count != anotherContainer.count {
return false
} // 检查两个Container相应位置的元素彼此是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
} // 如果所有元素检查都相同则返回true
return true
}

上面函数的详细解释:

这个函数有两个参数,类型分别用了泛型:C1、C2,但是对C1,C2做了类型约束。

  • C1,C2必须遵循Container协议。
  • C1的ItemType类型与C2的ItemType类型相同。
  • C1必须遵循Equatable协议。

第二条和第三条需要使用where语句,where语句的语法是 <泛型名 where 第一个约束, 第二个约束>。

学习Swift -- 泛型的更多相关文章

  1. Swift泛型协议的N种用法

    They said "you should learn a new language every year," so I  learned Swift. Now  I  learn ...

  2. ios -- 教你如何轻松学习Swift语法(一)

    目前随着公司开发模式的变更,swift也显得越发重要,相对来说,swift语言更加简洁,严谨.但对于我来说,感觉swift细节的处理很繁琐,可能是还没适应的缘故吧.基本每写一句代码,都要对变量的数据类 ...

  3. ios开发ios9新特性关键字学习:泛型,逆变,协变,__kindof

    一:如何去学习?都去学习什么? 1:学习优秀项目的设计思想,多问几个为什么,为什么要这么设计,这么设计的好处是什么,还能不能在优化 ,如何应用到自己的项目中 2:学习优秀项目的代码风格,代码的封装设计 ...

  4. ios -- 教你如何轻松学习Swift语法(三) 完结篇

    前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦.      一.自动引用计数   1.自动引用计数工作机制      1.1 swift和o ...

  5. ios -- 教你如何轻松学习Swift语法(二)

    前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦.      一.可选类型(重点内容)   1.什么是可选类型?        1.1在OC开 ...

  6. 一步一步学习Swift之(一):关于swift与开发环境配置

    一.什么是Swift? 1.Swift 是一种新的编程语言,用于编写 iOS 和 OS X 应用. 2.Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制. 3.Sw ...

  7. 開始学习swift开发

    近期要開始学习swift开发了,接下来的日子,会记录学习swift的历程.

  8. 学习swift语言的快速入门教程推荐

    随着苹果产品越来越火爆,苹果新推出的swift必定将在很大程度上代替oc语言.学好swift语言,对于IOS工程师来讲,已经是一门必备技能. 有一些比较好的英文版教程,值得学习. 1. Swift T ...

  9. 一步一步学习Swift之(二):好玩的工具playground与swfit基础语法

    playground好于在于能一边写代码一边看到输出的常量变量的值.不需要运行模拟器. 我们来试一下该工具的用法. 打开xcode6开发工具,选择Get started with a playgrou ...

随机推荐

  1. 圣诞节来了,雪花纷飞的CSS3动画,还不首页用起来

    圣诞节来了,冬天来了,怎么可以没有雪花纷飞效果,昨天下班前折腾了一会儿,弄了个雪花纷飞的实例,有兴趣的可以交流分享下. 原文链接:http://www.html5think.com/article/i ...

  2. 使用PHPmailer发送邮件的详细代码

    一.使用PHPMailer发送邮件的原因 PHP有内置的mail()方法,但是由于一些主机空间不支持该方法,所以经常会遇到无法发送邮件的情况. 所以,可以下载PHPMailer类,实现邮件发送. 二. ...

  3. [Oracle] - 性能优化工具(5) - AWRSQL

    在AWR中定位到问题SQL语句后想要了解该SQL statement的详细运行计划,于是就用AWR报告中得到的SQL ID去V$SQL等几个动态性能视图中查询,但发现V$SQL或V$SQL_PLAN视 ...

  4. shell 脚本执行日志通用模块

    目标 实现记录SHELL执行的開始时间,结束时间.执行状态,错误信息等,以函数封装日志记录的方式,脚本调用函数 源代码 通用函数脚本program_log_new.sh function init_l ...

  5. java基础入门-arraylist存储开销

    今天我们来看一下arraylist的存储开销,由于在项目其中,我尝试了一个很大的arraylist.然后内存爆了 所以我看了下源代码.原来arraylist的存储开销是比較大的,先上代码 import ...

  6. Python 基础学习

    http://www.cnblogs.com/Wxtrkbc/p/5486253.html

  7. Java基础知识强化之集合框架笔记54:Map集合之HashMap集合(HashMap<String,String>)的案例

    1. HashMap集合 HashMap集合(HashMap<String,String>)的案例 2. 代码示例: package cn.itcast_02; import java.u ...

  8. vs2010 Express 下载连接

    记录: vs2010  Express 下载连接 正式下载链接: http://download.microsoft.com/download/5/C/1/5C156922-CA10-49D8-B7E ...

  9. 卸载AMH 5.0面板的具体办法

    安装AMH 5.0面板只有YES.NO和EXIT,和AMH 4.X的安装.卸载.退出有点不同,那么如何卸载AMH 5.0面板呢? 1.root登录ssh 2.输入如下命令: killall php-f ...

  10. Linux下rsync增加SSH端口号的用法

    rsync默认使用SSH的22号端口,为了安全起见,很多机器更改了SSH默认的端口号,对应rsync命令的用法为: rsync -e 'ssh -p 1234' username@hostname:S ...