在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
泛型代码可以确保你写出灵活的,可重用的函数和定义出任何你所确定好的需求的类型。你可以写出避免重复的代码,并且用一种清晰的,抽象的方式表达出来。 泛型是Swift许多强大特征中的其中一个,许多Swift标准库是通过泛型代码构建出来的。事实上,你已经使用泛型贯穿着整个Language Guide,即便你没有实现它。例如:Swift的Array和Dictionary类型都是泛型集。你可以创建一个Int数组,也可创建一个String数组,或者甚至于可以是任何其他Swift的类型数据数组。同样的,你也可以创建存储任何指定类型的字典(dictionary),而且这些类型可以是没有限制的。 泛型所解决的问题这里是一个标准的,非泛型函数swapTwoInts,用来交换两个Int值: func swapTwoInts(inout a: Int, inout b: Int)
let temporaryA = a
a = b
b = temporaryA
}
这个函数使用in-out参数交换a和b的值,这两个参数被描述为In-Out类型参数。
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3"
func swapTwoStrings(inout a: String, inout b: String) {
let temporaryA = a
a = b
b = temporaryA
}
func swapTwoDoubles(inout a: Double, inout b: Double) {
let temporaryA = a
a = b
b = temporaryA
}
你可能注意到 但实际应用中通常需要一个用处更强大并且尽可能的考虑到更多的灵活性单个函数,可以用来交换两个任何类型值,很幸运的是,泛型代码帮你解决了这种问题。(一个这种泛型函数后面已经定义好了。)
泛型函数
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a
a = b
b = temporaryA
}
func swapTwoInts(inout a: Int, inout b: Int)
func swapTwoValues<T>(inout a: T, inout b: T)
这个函数的泛型版本使用了节点类型命名(通常此情况下用字母T来表示)来代替实际类型名(如Int、String或Double)。节点类型名并不是表示T必须是任何类型,但是其规定a和b必须是同一类型的T,而不管T表示任何类型。只有 另外一个不同之处在于这个泛型函数名后面跟着的节点类型名(T)是用尖括号括起来的()。这个尖括号告诉Swift那个T是
在下面的两个例子中,T分别代表Tnt和String: var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt is now 107, and anotherInt is now 3
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString is now "world", and anotherString is now "hello"
Type Parameters类型参数在上面的 一旦一个类型参数被指定,那么其可以被使用来定义一个函数的参数类型(如 你可支持多个类型参数,命名在尖括号中,用逗号分开。 命名类型参数在简单的情况下,泛型函数或泛型类型需要指定一个节点类型(如上面的 如果你使用多个参数定义更复杂的泛型函数或泛型类型,那么使用更多的描述类型参数是非常有用的。例如,Swift字典(Dictionary)类型有两个类型参数,一个是key,另外一个是值。如果你自己写字典,你或许会定义这两个类型参数为KeyType和ValueType,用来记住它们在你的泛型代码中的作用。
泛型类型通常在泛型函数中,Swift允许你定义你自己的泛型类型。这些自定义类、结构体和枚举作用于任何类型,如同 这部分向你展示如何写一个泛型集类型-
下图展示了一个栈的压栈(push)/出栈(pop)的行为:
这里展示了如何写一个非泛型版本的栈, struct IntStack {
var items = Int[]()
mutating func push(item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
}
这个结构体在栈中使用一个 上面所展现的 这里是一个相同代码的泛型版本: struct Stack<T> {
var items = T[]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
注意到 T定义了一个名为“某种类型T”的节点提供给后来用。这种将来类型可以在结构体的定义里任何地方表示为“T”。在这种情况下,T在如下三个地方被用作节点:
当创建一个新单例并初始化时, 通过用一对紧随在类型名后的尖括号里写出实际指定栈用到类型,创建一个 var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
// 现在栈已经有4个string了
下图将展示 从栈中pop并移除值"cuatro": let fromTheTop = stackOfStrings.pop()
// fromTheTop is equal to "cuatro", and the stack now contains 3 strings
由于 类型约束
例如,Swift的 这个需求强制加上一个类型约束作用于 当你创建自定义泛型类型时,你可以定义你自己的类型约束,当然,这些约束要支持泛型编程的强力特征中的多数。抽象概念如 类型约束语法你可以写一个在一个类型参数名后面的类型约束,通过冒号分割,来作为类型参数链的一部分。这种作用于泛型函数的类型约束的基础语法如下所示(和泛型类型的语法相同): func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
上面这个假定函数有两个类型参数。第一个类型参数 类型约束行为这里有个名为 func findStringIndex(array: String[], valueToFind: String) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findStringIndex(strings, "llama") {
println("The index of llama is \(foundIndex)")
}
// prints "The index of llama is 2"
如果只是针对字符串而言查找在数组中的某个值的索引,用处不是很大,不过,你可以写出相同功能的泛型函数 这里展示如何写一个你或许期望的 func findIndex<T>(array: T[], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
上面所写的函数不会编译。这个问题的位置在等式的检查上, 不过,所有的这些并不会让我们无从下手。Swift标准库中定义了一个 任何 func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
findIndex函数现在则可以成功的编译过,并且作用于任何遵循Equatable的类型,如Double或String: let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
// doubleIndex is an optional Int with no value, because 9.3 is not in the array
let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
// stringIndex is an optional Int containing a value of 2
关联类型当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型给定作用于协议部分的类型一个节点名(或别名)。作用于关联类型上实际类型是不需要指定的,直到该协议接受。关联类型被指定为 关联类型行为这里是一个 protocol Container {
typealias ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
这个协议没有指定容器里item是如何存储的或何种类型是允许的。这个协议只指定三个任何遵循 任何遵循 为了定义这三个条件, 为了达到此目的, 这里是一个早前IntStack类型的非泛型版本,适用于遵循Container协议: struct IntStack: Container {
// original IntStack implementation
var items = Int[]()
mutating func push(item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// conformance to the Container protocol
typealias ItemType = Int
mutating func append(item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
此外, 感谢Swift类型参考,你不用在 你也可以生成遵循 struct Stack<T>: Container {
// original Stack<T> implementation
var items = T[]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
// conformance to the Container protocol
mutating func append(item: T) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> T {
return items[i]
}
}
这个时候,节点类型参数 扩展一个存在的类型为一指定关联类型在Adding Protocol Conformance with an Extension中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。 Swift的 extension Array: Container {}
如同上面的泛型 Where 语句Type Constraints中描述的类型约束确保你定义关于类型参数的需求和一泛型函数或类型有关联。 对于关联类型的定义需求也是非常有用的。你可以通过这样去定义where语句作为一个类型参数队列的一部分。一个where语句使你能够要求一个关联类型遵循一个特定的协议,以及(或)那个特定的类型参数和关联类型可以是相同的。你可写一个where语句,通过紧随放置 下面的列子定义了一个名为 这两个容器可以被检查出是否是相同类型的容器(虽然它们可以是),但他们确实拥有相同类型的items。这个需求通过一个类型约束和where语句结合来表示: func allItemsMatch<
C1: Container, C2: Container
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1, anotherContainer: C2) -> Bool {
// check that both containers contain the same number of items
if someContainer.count != anotherContainer.count {
return false
}
// check each pair of items to see if they are equivalent
for i in 0..someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
// all items match, so return true
return true
}
这个函数用了两个参数: 这个函数的类型参数列紧随在两个类型参数需求的后面:
第三个和第四个要求被定义为一个where语句的一部分,写在关键字 这些要求意思是:
第三个和第四个要求结合起来的意思是 这些要求能够使
检查完之后,函数通过 如果循环体结束后未发现没有任何的不匹配,那表明两个容器匹配,函数返回true。 Here’s how the allItemsMatch function looks in action: 这里演示了allItemsMatch函数运算的过程: var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
var arrayOfStrings = ["uno", "dos", "tres"]
if allItemsMatch(stackOfStrings, arrayOfStrings) {
println("All items match.")
} else {
println("Not all items match.")
}
// prints "All items match."
上面的例子创建一个 |
请发表评论