The other difference between a generic function and a nongeneric function is that the generic function’s name (swapTwoValues(_:_:)) is followed by the placeholder type name (T) inside angle brackets (<T>). The brackets tell Swift that T is a placeholder type name within the swapTwoValues(_:_:) function definition. Because T is a placeholder, Swift doesn’t look for an actual type called T.
- func swapTwoValues<T>(_ a: inout T, _ b: inout 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)
协议不能初始化,所以协议中的泛型使用inffer技术和typealias技术来指定
Generic Types
- struct Stack<Element> {
- var items = [Element]()
- mutating func push(_ item: Element) {
- items.append(item)
- }
- mutating func pop() -> Element {
- return items.removeLast()
- }
- }
- var stackOfStrings = Stack<String>()
- stackOfStrings.push("uno")
- stackOfStrings.push("dos")
- stackOfStrings.push("tres")
- stackOfStrings.push("cuatro")
- // the stack now contains 4 strings
Extending a Generic Type
- extension Stack {
- var topItem: Element? {
- return items.isEmpty ? nil : items[items.count - 1]
- }
- }
Type Constraints
- func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
- for (index, value) in array.enumerated() {
- if value == valueToFind {
- return index
- }
- }
- return nil
- }
- let doubleIndex = findIndex(of: 9.3, in: [3.14159, 0.1, 0.25])
- // doubleIndex is an optional Int with no value, because 9.3 isn't in the array
- let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"])
- // stringIndex is an optional Int containing a value of 2
Associated Types
- protocol Container {
- associatedtype Item
- mutating func append(_ item: Item)
- var count: Int { get }
- subscript(i: Int) -> Item { get }
- }
- 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 Item = Int
- mutating func append(_ item: Int) {
- self.push(item)
- }
- var count: Int {
- return items.count
- }
- subscript(i: Int) -> Int {
- return items[i]
- }
- }
type inference
Thanks to Swift’s type inference, you don’t actually need to declare a concrete Item of Int as part of the definition of IntStack. Because IntStack conforms to all of the requirements of the Container protocol, Swift can infer the appropriate Item to use, simply by looking at the type of the append(_:) method’s item parameter and the return type of the subscript. Indeed, if you delete the typealias Item = Int line from the code above, everything still works, because it’s clear what type should be used for Item.
- struct Stack<Element>: Container {
- // original Stack<Element> implementation
- var items = [Element]()
- mutating func push(_ item: Element) {
- items.append(item)
- }
- mutating func pop() -> Element {
- return items.removeLast()
- }
- // conformance to the Container protocol
- mutating func append(_ item: Element) {
- self.push(item)
- }
- var count: Int {
- return items.count
- }
- subscript(i: Int) -> Element {
- return items[i]
- }
- }
Extending an Existing Type to Specify an Associated Type
- extension Array: Container {}
Array’s existing append(_:) method and subscript enable Swift to infer the appropriate type to use for Item, just as for the generic Stack type above. After defining this extension, you can use any Array as a Container.
Adding Constraints to an Associated Type
- protocol Container {
- associatedtype Item: Equatable
- mutating func append(_ item: Item)
- var count: Int { get }
- subscript(i: Int) -> Item { get }
- }
To conform to this version of Container, the container’s Item type has to conform to the Equatable protocol.
Using a Protocol in Its Associated Type’s Constraints
A protocol can appear as part of its own requirements. For example, here’s a protocol that refines the Container protocol, adding the requirement of a suffix(_:) method. The suffix(_:) method returns a given number of elements from the end of the container, storing them in an instance of the Suffix type.
- protocol SuffixableContainer: Container {
- associatedtype Suffix: SuffixableContainer where Suffix.Item == Item
- func suffix(_ size: Int) -> Suffix
- }
In this protocol, Suffix is an associated type, like the Item type in the Container example above. Suffix has two constraints: It must conform to the SuffixableContainer protocol (the protocol currently being defined), and its Item type must be the same as the container’s Item type. The constraint on Item is a generic where clause, which is discussed in Associated Types with a Generic Where Clause below.
Here’s an extension of the Stack type from Strong Reference Cycles for Closures above that adds conformance to the SuffixableContainer protocol:
- extension Stack: SuffixableContainer {
- func suffix(_ size: Int) -> Stack {
- var result = Stack()
- for index in (count-size)..<count {
- result.append(self[index])
- }
- return result
- }
- // Inferred that Suffix is Stack.
- }
- var stackOfInts = Stack<Int>()
- stackOfInts.append(10)
- stackOfInts.append(20)
- stackOfInts.append(30)
- let suffix = stackOfInts.suffix(2)
- // suffix contains 20 and 30
Generic Where Clauses Type constraints
- func allItemsMatch<C1: Container, C2: Container>
- (_ someContainer: C1, _ anotherContainer: C2) -> Bool
- where C1.Item == C2.Item, C1.Item: Equatable {
- // 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're equivalent.
- for i in 0..<someContainer.count {
- if someContainer[i] != anotherContainer[i] {
- return false
- }
- }
- // All items match, so return true.
- return true
- }
- extension Container where Item: Equatable {
- func startsWith(_ item: Item) -> Bool {
- return count >= 1 && self[0] == item
- }
- }
Associated Types with a Generic Where Clause
- protocol Container {
- associatedtype Item
- mutating func append(_ item: Item)
- var count: Int { get }
- subscript(i: Int) -> Item { get }
- associatedtype Iterator: IteratorProtocol where Iterator.Element == Item
- func makeIterator() -> Iterator
- }