泛型

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

泛型所解决的问题

先来看一个交换两个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. Linux主机安全配置的几个脚本【转载】

    标签:linux Linux主机安全配置的几个脚本 职场 休闲原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://hx100.blog ...

  2. C# 的异步

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.N ...

  3. 简约的返回顶部效果(jQuery)

    博客建好也快一个月了,主题是在原有主题的基础上做的修改,喜欢这样的清新简约风格,为了更好的体验,以后也会经常做修改. 一般博客的文章都比较长,看到下面不能快速返回到顶部还真是不方便,今天自己动手写了一 ...

  4. Android Dev

    [2015-04-22 13:30:37 - branch_1.5.1_WelcomeActivity] R.java was modified manually! Reverting to gene ...

  5. backpropagate

    http://blog.csdn.net/celerychen2009/article/details/8964753

  6. java final keyword

    依据上下文环境,java的keywordfinal也存在着细微的差别,但通常指的是“这是无法改变的.”不想改变的理由由两种:一种是效率,还有一种是设计.因为两个原因相差非常远,所以关键子final可能 ...

  7. 【转】学习Flex ActionScript 3.0 强烈推荐电子书

    学习Flex ActionScript 3.0 强烈推荐电子书 AdvancED ActionScript 3.0 Animation(<Make things  move>姐妹篇,强烈推 ...

  8. iOS开发中.pch 文件的使用及其相关工程设置

    .pch文件 也是一个头文件,pch头文件的内容能被项目中的其他所有源文件共享和访问.是一个预编译文件. 首先说一下pch的作用: 1.存放一些全局的宏(整个项目中都用得上的宏) 2.用来包含一些全部 ...

  9. 随便说一说bootstrap-table插件

    如题... bootstrap-table插件是一个js的表格插件 找了一下资料发现并没有多少 这里放一下初始化的语法 这里在html中写一个目标table元素 <table id=" ...

  10. Java 非对称加密

    package test; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.Object ...