写在前面:
泛型,类似于OC中的id类型,它可以指代任何一种类型,并不是在编译时确定其类型,而是在运行时确定其实际类型。使用泛型的函数,我们称之为泛型函数
intout关键字
现有两变量,要求其值互换
var someStr = "I am A"
var anotherStr = "I am B"
如果方法中参数得值在方法内部不会发生变化
即参数为常量,用let声明,let可省略
func swapTwoStrings1( a: String, b: String) {
let tempA = b //对形参a的值不发生变化
let tempB = a //对形参b的值不发生变化
}
someStr //"I am A" 对实参someStr的值不发生变化
anotherStr //"I am B" 对实参anotherStr的值不发生变化
如果方法中的参数的值在方法内部会发生变化
参数需声明为变量,用var声明,var不可省略
func swapTwoStrings2(var a: String, var b: String) {
let temporaryA = a
a = b //方法内a的值发生了改变
b = temporaryA //方法内b的值发生了改变
}
swapTwoStrings2(someStr, b: anotherStr)
someStr //"I am A" 方法外someStr的值未改变
anotherStr //"I am B" 方法外anotherStr的值未改变
如果想让传入的参数在进入方法后发生改变(参数在进入方法后值发生真正改变)
使用inout关键字(使形参和实参指向的内存一致)
func swapTwoStrings3(inout a: String, inout b: String) {
let temporaryA = a
a = b //方法内a的值发生了改变
b = temporaryA //方法内b的值发生了改变
}
//传递参数时多出现了"&"符号,其实传递了字符串的地址
swapTwoStrings3(&someStr, b: &anotherStr)
someStr //"I am B" 方法外someStr的值发生改变
anotherStr //"I am A" 方法外anotherStr的值发生改变
注意:inout修饰的参数是不能有默认值的,有范围的参数集合也不能被修饰,另外,一个参数一旦被inout修饰,就不能再被var和let修饰了
泛型
上面的方法是为了把两个String变量的值相互交换
现要求把两个Int变量的值相互交换
func swapTwoInts(inout a: Int, inout b: Int){
let temporaryA = a
a = b
b = temporaryA
}
从方法结构来看,swapTwoStrings3和swapTwoInts两个方法仅仅是参数类型不同,想办法合并他们
//T代表占位类型,可以是任何一种类型,
//a与b类型都是T,T只能是一种类型,所以a与b的类型一定要相同
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a
a = b
b = temporaryA
}
swapTwoValues(&someStr, b: &anotherStr) //传入两个String类型
var someInt = 4
var anotherInt = 8
swapTwoValues(&someInt, b: &anotherInt)//传入两个Int类型
//swapTwoValues(&someStr, b: &anotherInt) //报错a和b类型不同
使用多个泛型类型参数(在"<>"内用","分隔)
func toBeDict<KeyType,ValueType>(inout a: KeyType, inout b: ValueType) {
Dictionary(dictionaryLiteral: (String(a), b))
}
toBeDict(&someStr,b: &someInt) //输出["I am A": 8]
泛型枚举
enum OptionalValue<T> {
case None
case Some(T)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)
用泛型来建立一个栈
//栈和Array相似,一个数组可以允许其里面任何位置的插入/删除操作,而栈,只允许在集合的末端添加新的项(如同push一个新值进栈)。同样的一个栈也只能从末端移除项(如同pop一个值出栈)
struct Stack<T> {
//创建一个名为items的属性,使用空的T类型值数组对其进行初始化
var items = [T]()
//指定一个包含一个参数名为item的push方法,该参数必须是T类型
mutating func push(item: T) {
items.append(item)
}
//指定一个pop方法的返回值,该返回值将是一个T类型值
mutating func pop() -> T {
return items.removeLast()
}
}
var stackOfStrings = Stack<String>() //一个String栈
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.pop() //"dos"
stackOfStrings.pop() //"uno"
泛型约束语法
第一种,泛型类型后面追加协议
//Equatable协议规定了T的类型一定能使用"=="和"!="
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
for (index, value) in EnumerateSequence(array) {
if value == valueToFind {
return index
}
}
return nil
}
findIndex([0,2,3,1], valueToFind: 1)
第二种,使用where约束
在实际使用where前,谈一下typealias(关联类型)
/*
typealias是一种常用于协议的特殊泛型,它可以使协议适用于更多情况
常规的写法应该是typealias MyInt = Int。表示给Int取一个别名为MyInt。
单独写typealias ItemType 代表给任意类型取别名为ItemType
协议中的关联类型的实际类型是不需要指定的,直到该协议接受。
注:这里的typealias不是用于约束泛型,而是为了下面的where约束泛型而服务的
*/
protocol Container {
typealias ItemType
//对象必须可以通过append方法添加一个新item到容器里;
mutating func append(item: ItemType)
//对象必须可以使用count属性获取容器里items的数量,并返回一个Int值
var count: Int { get } //get代表只读属性
//对象必须可以通过容器的Int索引值下标可以检索到每一个item
subscript(i: Int) -> ItemType { get }
}
//注意,这里满足Container约束的对象是StackPro结构体,而不是T
struct StackPro<T>: Container {
var items = [T]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
mutating func append(item: T) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> T {
return items[i]
}
}
var stackProOfStrings = StackPro<String>() //一个String栈
stackProOfStrings.push("uno")
stackProOfStrings.push("dos")
stackProOfStrings.count
扩展一个存在的类型为一指定关联类型(为了下面Array有ItemType这个属性)
//Array已经满足Container协议中的三个方法,其中的ItemType属性又是在运行时自动确定的类型,所以我们写一个空扩展就行了
extension Array: Container {}
//使用where约束
func allItemsMatch<
C1: Container, C2: Container
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1,_ anotherContainer: C2) -> Bool {
if someContainer.count != anotherContainer.count {
return false
}
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
return true
}
var stackProOfStrings2 = StackPro<String>()
stackProOfStrings2.push("uno")
stackProOfStrings2.push("dos")
stackProOfStrings2.push("tres")
var arrayOfStrings = ["uno", "dos", "tres"]
//按这么绕了一大圈之后arrayOfStrings和stackProOfStrings2这两个类型不同的对象可以比较了
if allItemsMatch(stackProOfStrings2, arrayOfStrings) {
print("All items match.") //输出 All items match
} else {
print("Not all items match.")
}