1、Swift入门学习笔记(第一版),对Swift的基础知识点进行梳理总结。知识点一直在变,只是作为参考,以苹果官方文档为准~
2、在学习完基本的知识点以后会结合官方文档及相关资料,在此版本的基础上进行添加更改。
二十三、泛型
根据需求定义、适用任何类型的、灵活且可重用的函数和类型。避免重复代码。
泛型代码贯穿整个Swift
,例如数组,字典类型都是泛型集,因为你可创建Int
,String
各种不同类型的数组
1、泛型函数
1.1、交换两个值实例
例如交换两个Int
值的常规写法
func swapTwoInts(inout a:Int,inout _ b:Int) {
let temporary = a
a = b
b = temporary
}
var oneInt = 2
var theOtherInt = 18
print("BeforeSwap -> oneInt:\(oneInt),theOtherInt:\(theOtherInt)")
swapTwoInts(&oneInt,&theOtherInt)
print("AfterSwap -> oneInt:\(oneInt),theOtherInt:\(theOtherInt)")
Output:
BeforeSwap -> oneInt:2,theOtherInt:18
AfterSwap -> oneInt:18,theOtherInt:2
但是有时候我们要交换两个Double
值,两个String
,就要写更多的函数,但是实际上实现功能相同,只是类型不同
因此可以利用泛型函数,可灵活的应用于任何类型
func swapTwoValues<T>(inout a:T,inout _ b:T) {
let temporary = a
a = b
b = temporary
}
var oneString = "oneString"
var theOtherString = "theOtherString"
print("BeforeSwap -> oneString:\(oneString),theOtherString:\(theOtherString)")
swapTwoValues(&oneString,&theOtherString)
print("AfterSwap -> oneString:\(oneString),theOtherString:\(theOtherString)")
Output:
BeforeSwap -> oneString:oneString,theOtherString:theOtherString
AfterSwap -> oneString:theOtherString,theOtherString:oneString
1.2、语法说明
func swapTwoValues<T>(inout a:T,inout _ b:T)
使用了占位类型名字(通常用字母T
表示)来代替实际类型名,a,b
必须是同一类型T
占位类型要用尖括号括起来<T>
,告诉Swift
其中的T
是函数所定义的一个类型,这样编译器就不会去查找命名为T的实际类型了
占位类型名字可以是任何有效的标识符
注意:swap函数,swift标准库本来就有自带的。功能与上面的函数相同
swap(&oneString,&theOtherString) print("SwapAgain -> oneString:\(oneString),theOtherString:\(theOtherString)")
Output:
SwapAgain -> oneString:oneString,theOtherString:theOtherString
2、泛型类型
创建一个泛型集类型:栈-pop
-push
struct Stack<T> {
var items = [T]()
mutating func push(item:T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
注意:定义的时候要制定所属类型
var stackOfStrings = Stack<String>()
stackOfStrings.push("A")
stackOfStrings.push("B")
stackOfStrings.push("C")
stackOfStrings.push("D")
print(stackOfStrings.items)
let fromTheTop = stackOfStrings.pop()
print(fromTheTop)
Output:
["A","B","C","D"]
D
3、扩展一个泛型类型
泛型的扩展中可以使用原始类型的参数
extension Stack {
var topItem:T? {
return items.isEmpty ? nil : items[items.count-1]
}
}
let topItem = stackOfStrings.topItem
print(topItem!)
Output:
C
4、类型约束
有时候使用泛型类型或者泛型函数上的类型需要强制约定为某种特定类型,譬如必须继承自某指定类的类型参数,或遵循一个特定的协议或协议构成
例如Swift
中字典类型,其键类型必须可哈希确保它是唯一的,遵循Hashable
协议。实际上所有的Swift
基本类型(String
,Int
,Double
和Bool
)都是默认可哈希的
4.1、类型约束语法
func someFunction<T: SomeClass,U: SomeProtocol>(someT: T,someU: U) {
// 这里是函数主体
}
Equatable协议
官方文档提到了一个Swift
的标准库协议,Equatable
协议,遵循此协议,可以利用等式符(==
)和不等符(!=
)对任何两个该类型进行比较。因为通常并不是所有类型都能用==
,!=
来进行比较,遵循此协议然后进行比较
5、关联类型
定义协议时,声明一个或多个关联类型作为协议定义的一部分。
给定类型的一个占位名,不用指定其实际类型,指定为typealias
关键字
简单地说就是:类型的一个占位符,等到具体实现协议的时候才知道它具体是什么类型
定义一个容器协议,包含一个ItemType
关联类型
protocol Container {
typealias ItemType
mutating func append(item:ItemType)
var count:Int{ get }
}
//遵循Container协议的泛型Stack类型
struct Stack<T>:Container {
var items = [T]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
//Swift会自动推断出被用作关联类型ItemType类型是T,不用显式地写出.
//但是你写出来也没事 typealias ItemType = T
mutating func append(item: T) {
self.push(item)
}
var count:Int {
return items.count
}
}
var stackOfStrings = Stack<String>()
stackOfStrings.push("A")
stackOfStrings.push("B")
stackOfStrings.push("C")
stackOfStrings.push("D")
print(stackOfStrings.items)
let fromTheTop = stackOfStrings.pop()
print(fromTheTop,stackOfStrings.count)
Output:
["A","D"]
D 3
6、扩展一个存在的类型为一指定关联类型
之前又讲到,一个类都实现了协议的内容,但是还没遵循,可以通过空扩展来实现遵循
这里也一样,比如Swift
自带的类Array
都实现了Container
的要求,因此可以扩展Array
去遵循Container
下而已,简单声明即可
extension Array:Container { }
定义此扩展后,可以将任何Array当做Container使用
7、Where语句
补充定义参数的约束。例如让一个关联类型遵循某个特定的协议,或特定的类型参数与关联类型相同
也可以写一个针对一个或多个关联参数的约束,以及一个或多个类型和关联类型间的等价关系
func allItemsMath<C1:Container,C2:Container where C1.ItemType == C2.ItemType,C1.ItemType:Equatable>(someContainer:C1,anotherContainer:C2) {
//函数内容
}
上述约束描述:
C1
,C2
遵循Container
协议
C1
的ItemType
遵循Equatable
协议
C1
的ItemType
类型与C2
的ItemType
相同