泛型

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

泛型所解决的问题

先来看一个交换两个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. UserControl调用Umbraco的Data Types

    本篇文章介绍的是基于Umbraco CMS技术搭建的网站所使用的相关技术. 1.  需求: 网站前台功能有个表单提交,表单控件用到下拉列表(dropdownlist),需求是在dropdownlist ...

  2. Cocos2d-x3.0TestCpp文件夹笔记(二)

    3.Actions-Basic:此demo中体现ccp由Point取代 ①ActionManual:直接设置精灵的属性demo. const Color3B Color3B::RED    (255, ...

  3. Python介绍、环境搭建(Eclipse插件)、第一个程序

    Python介绍 特点 优雅.明白.简单. 适合领域 1. Web站点和各种网络服务 2. 系统工具和脚本 3. 作为"胶水"语言把其它语言开发的模块包装起来方便使用 和其它语言对 ...

  4. Android 自定义View修炼-自定义可动画展开收缩View的实现

    有时候需要点击一个view可以动画展开和收缩折叠一个View这样的效果,这样就可以直接自定义View来实现. 本例中,采用继承FrameLayout来实现自定义的ExpandView.下面将详细介绍各 ...

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

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

  6. Chapter 3. Installing Gradle 安装gradle

    3.1. Prerequisites Gradle requires a Java JDK or JRE to be installed, version 6 or higher (to check, ...

  7. Microsoft Visual Studio Professional 2012 专业版 下载

    记录(以下内容来自网络收集): 下载地址: https://www.microsoft.com/zh-cn/download/details.aspx?id=30682 直接iso连接下载址: htt ...

  8. CentOS7使用Redis

    使用Python操作Redis 安装pip # yum install python-pip 升级pip # pip install --upgrade pip 安装redis-py库 # pip i ...

  9. 删除mysql重复记录的办法

    网上有很多的办法,但是大多数都是通过临时表的办法,其实你是可以用一句简单的sql就可以做到: alter ignore table SOMETABLE add primary key(fields n ...

  10. FragmentTransaction.addToBackStack无效的问题

    FragmentTransaction.addToBackStack无效的问题: 如果当前的类继承的ActionBarActivity,则FragmentManager必须来自v4包,这样addToB ...