学习Swift -- 泛型
泛型
泛型代码可以让你写出根据自我需求定义、适用于任何类型的,灵活且可重用的函数和类型。它的可以让你避免重复的代码,用一种清晰和抽象的方式来表达代码的意图。
泛型所解决的问题
先来看一个交换两个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 基本类型(如String
,Int
, 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 -- 泛型的更多相关文章
- Swift泛型协议的N种用法
They said "you should learn a new language every year," so I learned Swift. Now I learn ...
- ios -- 教你如何轻松学习Swift语法(一)
目前随着公司开发模式的变更,swift也显得越发重要,相对来说,swift语言更加简洁,严谨.但对于我来说,感觉swift细节的处理很繁琐,可能是还没适应的缘故吧.基本每写一句代码,都要对变量的数据类 ...
- ios开发ios9新特性关键字学习:泛型,逆变,协变,__kindof
一:如何去学习?都去学习什么? 1:学习优秀项目的设计思想,多问几个为什么,为什么要这么设计,这么设计的好处是什么,还能不能在优化 ,如何应用到自己的项目中 2:学习优秀项目的代码风格,代码的封装设计 ...
- ios -- 教你如何轻松学习Swift语法(三) 完结篇
前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦. 一.自动引用计数 1.自动引用计数工作机制 1.1 swift和o ...
- ios -- 教你如何轻松学习Swift语法(二)
前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦. 一.可选类型(重点内容) 1.什么是可选类型? 1.1在OC开 ...
- 一步一步学习Swift之(一):关于swift与开发环境配置
一.什么是Swift? 1.Swift 是一种新的编程语言,用于编写 iOS 和 OS X 应用. 2.Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制. 3.Sw ...
- 開始学习swift开发
近期要開始学习swift开发了,接下来的日子,会记录学习swift的历程.
- 学习swift语言的快速入门教程推荐
随着苹果产品越来越火爆,苹果新推出的swift必定将在很大程度上代替oc语言.学好swift语言,对于IOS工程师来讲,已经是一门必备技能. 有一些比较好的英文版教程,值得学习. 1. Swift T ...
- 一步一步学习Swift之(二):好玩的工具playground与swfit基础语法
playground好于在于能一边写代码一边看到输出的常量变量的值.不需要运行模拟器. 我们来试一下该工具的用法. 打开xcode6开发工具,选择Get started with a playgrou ...
随机推荐
- 设计 REST 风格的 MVC 框架
http://www.ibm.com/developerworks/cn/java/j-lo-restmvc/ 传统的 JavaEE MVC 框架如 Struts 等都是基于 Action 设计的后缀 ...
- Flume 入门--几种不同的Sources
1.flume概念 flume是分布式的,可靠的,高可用的,用于对不同来源的大量的日志数据进行有效收集.聚集和移动,并以集中式的数据存储的系统. flume目前是apache的一个顶级项目. flum ...
- android考试题
一.选择题 1. Math.round(11.5)等于多少( ). Math.round(-11.5) 等于多少( C ). A.11 ,-11 B.11 ,-12 C.12 ,-1 ...
- 【转】Android Studio Essential Training
http://ask.android-studio.org/?/explore/category-video Android Studio Essential Training内容包括:- Andro ...
- 在XMPP的JAVA开源实现Openfire中,增加LBS 附近的人功能
1. XMPP协议 与 Openfire XMPP协议是IM领域的标准协议了,具体可参考 http://xmpp.org 及RFC6120,RFC6121,RFC6122等相关文档. http: ...
- jdk和jre是什么?都有什么用?(转帖)
jdk和jre是什么?都有什么用?(转帖) 文章分类:Java编程 大家肯定在安装JDK的时候会有选择是否安装单独的jre,一般都会一起安装,我也建议大家这样做.由于这样更能帮助大家弄清楚它们的差别: ...
- linux 0.11 源码学习+ IO模型
http://www.cnblogs.com/Fredric-2013/category/696688.html
- careercup-数组和字符串1.8
1.8 假定有一个方法isSubstring,可检查一个单词是否为其他字符串的子串.给定两个字符串s1和s2,请编写代码检查s2是否为s1旋转而成,要求只能调用一次isSubstring.旋转字符串: ...
- vim阅读c++代码的快捷键
未完,待总结 1. 首先适用ctags建立当前工作目录的标签: cd /home/wanglc/WorkDirectory ctags -R or ctags -R * 命令结束以后,会生成一个tag ...
- dell笔记本通过uefi+gpt模式安装win10系统
安装前,需要确认dell笔记本是否支持uefi 1.使用UltraISO制作硬盘镜像后,过程如下 1) 选择"文件"->"打开",如下 2) 在打开的对话 ...