Swift学习笔记(十八)泛型
泛型 泛型代码可以让你写出根据自我需求定义、适用于任何类型的,灵活且可重用的函数和类型 泛型所解决的问题 定义一个swapTwoInts方法,用于交换两个int类型的值 一:swapTwoInts func swapTwoInts(inout a: Int,inout b: Int) { let temporaryA = a a = b b = temporaryA } 二:swapTwoStrings func swapTwoStrings(inout a: String,inout b: String) { let temporaryA = a a = b b = temporaryA } 三:swapTwoDoubles func swapTwoDoubles(inout a: Double,inout b: Double) { let temporaryA = a a = b b = temporaryA } 上述定义了三个方法swapTwoInts、swapTwoStrings、swapTwoDoubles功能都是相同的(交换两个数的值),唯一不同的是传入的变量类型不同 泛型函数(inout关键字表示参数是地址) func swapTwoValues<T>(inout a: T,inout b: T) { let temporaryA = a a = b b = temporaryA } 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" 在简单的情况下,泛型函数或泛型类型需要指定一个占位类型,通常用一单个字母T来命名类型参数。不过,你可以使用任何有效的标识符来作为类型参数名。 泛型类型 非泛型版本的栈 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() } } var stackOfStrings = Stack<String>() stackOfStrings.push("uno") stackOfStrings.push("dos") stackOfStrings.push("tres") stackOfStrings.push("cuatro") // 现在栈已经有4个string了 let fromTheTop = stackOfStrings.pop() // fromTheTop is equal to "cuatro",and the stack now contains 3 strings
泛型类型拓展 向泛型栈中添加了一个只读的计算属性topItem,这个属性返回一个可选类型T的值。若栈不为空,则返回栈顶元素 extension Stack { var topItem: T? { return items.isEmpty ? nil : items[items.count - 1] } } 类型约束 swapTwoValues函数和Stack类型可以作用于任何类型,不过,有的时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定类型是非常有用的。类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。 类型约束语法 func someFunction<T: SomeClass,U: SomeProtocol>(someT: T,someU: U) { //语法 } 上面这个假定函数有两个类型参数。第一个类型参数T,有一个需要T必须是SomeClass子类的类型约束;第二个类型参数U,有一个需要U必须遵循SomeProtocol协议的类型约束。
类型约束行为 名为findStringIndex的非泛型函数,该函数功能是去查找包含一给定String值的数组 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" Swift 标准库中定义了一个Equatable协议,该协议要求任何遵循的类型实现等式符(==)和不等符(!=)对任何两个该类型进行比较。所有的 Swift 标准类型自动支持Equatable协议。 func findIndex<T: Equatable>(array: [T],valueToFind: T) -> Int? { for (index,value) in enumerate(array) { if value == valueToFind { return index } } return nil } //T: Equatable,也就意味着“任何T类型都遵循Equatable协议”。 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 关联类型 当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型给定作用于协议部分的类型一个节点名(或别名)。作用于关联类型上实际类型是不需要指定的,直到该协议接受。关联类型被指定为typealias关键字 关联类型行为 protocol Container { typealias ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } Container协议定义了三个任何容器必须支持的兼容要求: 一、必须可能通过append方法添加一个新item到容器里; 二、必须可能通过使用count属性获取容器里items的数量,并返回一个Int值; 三、必须可能通过容器的Int索引值下标可以检索到每一个item。 struct EmpIntStack: 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] } } <span class="s1">//IntStack</span><span class="s2">指定了</span><span class="s1">Container</span><span class="s2">的实现,适用的</span><span class="s1">ItemType</span><span class="s2">被用作</span><span class="s1">Int</span><span class="s2">类型。对于这个</span><span class="s1">Container</span><span class="s2">协议实现而言,定义</span><span class="s1">typealias ItemType = Int</span><span class="s2">,将抽象的</span><span class="s1">ItemType</span><span class="s2">类型转换为具体的</span><span class="s1">Int</span><span class="s2">类型。</span> 你也可以生成遵循 Container 协议的泛型 Stack 类型: struct EmpStack<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] } } //这个时候,占位类型参数T被用作append方法的item参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的ItemType的T的合适类型 Where 语句 一个where语句使你能够要求一个关联类型遵循一个特定的协议,以及(或)那个特定的类型参数和关联类型可以是相同的。你可写一个where语句,通过紧随放置where关键字在类型参数队列后面,其后跟着一个或者多个针对关联类型的约束,以及(或)一个或多个类型和关联类型的等于关系。 下面的列子定义了一个名为allItemsMatch的泛型函数,用来检查是否两个Container单例包含具有相同顺序的相同元素。如果匹配到所有的元素,那么返回一个为true的Boolean值,反之,则相反。 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 } 这个函数用了两个参数:someContainer和anotherContainer。someContainer参数是类型C1,anotherContainer参数是类型C2。C1和C2是容器的两个占位类型参数,决定了这个函数何时被调用。 这个函数的类型参数列紧随在两个类型参数需求的后面: ①C1必须遵循Container协议 (写作 C1: Container)。 ②C2必须遵循Container协议 (写作 C2: Container)。 ③C1的ItemType同样是C2的ItemType(写作 C1.ItemType == C2.ItemType)。 ④C1的ItemType必须遵循Equatable协议 (写作 C1.ItemType: Equatable)。 ③和④要求被定义为一个where语句的一部分,写在关键字where后面,作为函数类型参数链的一部分。 var empStackOfStrings = Stack<String>() empStackOfStrings.push("uno") empStackOfStrings.push("dos") empStackOfStrings.push("tres") var arrayOfStrings = ["uno","dos","tres"] //if allItemsMatch(empStackOfStrings,arrayOfStrings) { // println("All items match.") //} else { // println("Not all items match.") //} // prints "All items match." (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- ruby-on-rails – 将一个控制器重定向到另一个控制器
- repo切换xml命令
- Jquery Ajax Error 调试错误的技巧
- ruby-on-rails – 在Rails中复制ActiveRecord,使用现有参数
- 如何在VC中调试未初始化的变量
- 看张亚飞《.Net for Flash FMS》的笔记
- Linq to XML Linq读取MXL(XDocument.Load()、XDocument.Pa
- database – 具有where子句行为的分层查询“START WITH”
- FreeSWITCH 拨号脚本之public.xml
- ruby-on-rails – 跨AWS扩展应用程序的单点故障