Latest web development tutorials

Swift Generics

Swift provides a generic flexible and allows you to write reusable functions and types.

Swift standard library is built out by generic code.

Swift arrays and dictionaries types are generic set.

You can create an Int array, you can create a String array, or even data array can be any type of other Swift.

The following example is a non-generic function exchange for the exchange of two Int values:

// 定义一个交换两个变量的函数
func exchange(inout a: Int, inout b: Int) {
    let temp = a
    a = b
    b = temp
}

var numb1 = 100
var numb2 = 200

print("交换前数据: \(numb1) 和 \(numb2)")
exchange(&numb1, b: &numb2)
print("交换后数据: \(numb1) 和 \(numb2)")

The above program execution output is:

交换前数据: 100 和 200
交换后数据: 200 和 100

Generic functions can access any type, such as Int or String.

The following example is a generic function for exchange swap two Int and String values:

func exchange<T>(inout a: T, inout b: T) {
    let temp = a
    a = b
    b = temp
}

var numb1 = 100
var numb2 = 200

print("交换前数据:  \(numb1) 和 \(numb2)")
exchange(&numb1, b: &numb2)
print("交换后数据: \(numb1) 和 \(numb2)")

var str1 = "A"
var str2 = "B"

print("交换前数据:  \(str1) 和 \(str2)")
exchange(&str1, b: &str2)
print("交换后数据: \(str1) 和 \(str2)")

The above program execution output is:

交换前数据:  100 和 200
交换后数据: 200 和 100
交换前数据:  A 和 B
交换后数据: B 和 A

Generic version of this function using the placeholder type name (usually represented by the letter T in this case) instead of the actual type names (such as Int, String or Double). Placeholder type name must not suggest what type T, but it suggests a and b must be the same type T, regardless of what type T represents. Only exchange (_: _ :) function when passed in each call type in order to determine the actual type T represents.

Another difference is that this latter function name followed by the generic name of the placeholder type (T) is enclosed in angle brackets ( ). The angle brackets told Swift that T is the exchange (_: _ :) a function of the type defined. Since T is a placeholder named type, Swift is not to find the name of the actual type T.


Generic type

Swift allows you to define your own generic types.

Custom classes, structures, and enumerations applied to any type as Array and Dictionary usage.

struct TOS<T> {
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T {
        return items.removeLast()
    }
}

var tos = TOS<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
print(tos.items)

tos.push("类型参数")
print(tos.items)

tos.push("类型参数名")
print(tos.items)


let deletetos = tos.pop()

The above program execution output is:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "类型参数"]
["Swift", "泛型", "类型参数", "类型参数名"]

Extended generic type

When you extend a generic type of time (extension using keywords), you do not need to provide a type parameter list in the extended definition. More convenient it is that primitive type parameter type definition declared in the list can be expanded in use and the parameter names from the original type will be used as a reference to the original definition of the type parameter.

Examples

The following example extends the generic TOS type, to add a read-only property named first calculation, it will return the current top of the stack elements and will not be removed from the stack.

struct TOS<T> {
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T {
        return items.removeLast()
    }
}

var tos = TOS<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
    print(tos.items)
    
    tos.push("类型参数")
    print(tos.items)
    
    tos.push("类型参数名")
    print(tos.items)

// 扩展泛型 TOS 类型
extension TOS {
    var first: T? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

if let first = tos.first {
    print("栈顶部项:\(first)")
}

The above program execution output is:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "类型参数"]
["Swift", "泛型", "类型参数", "类型参数名"]
栈顶部项:类型参数名

Type constraint

Type constraint specifies that a type parameter must inherit from the specified class or follow a specific protocol or composition.

Type constraint syntax

You can write a type constraint in the back of a type parameter names, divided by a colon, as part of the type parameter chain. Such acts on the basis of the type of constraint syntax for generic functions are as follows (the same generic type of syntax):

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 这里是函数主体
}

Examples

// 函数可以作用于查找一字符串数组中的某个字符串
func findStringIndex(array: [String], _ valueToFind: String) -> Int? {
    for (index, value) in array.enumerate() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findStringIndex(strings, "llama") {
    print("llama 的下标索引值为 \(foundIndex)")
}

The above program execution output is:

llama 的下标索引值为 2

Examples of the type of association

Swift typealias keyword used to set the type of association.

Define a protocol, sometimes declare one or more associated type of protocol part of the definition is very useful as.

protocol Container {
    // 定义了一个ItemType关联类型
    typealias ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

// 遵循Container协议的泛型TOS类型
struct TOS<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]
    }
}

var tos = TOS<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
print(tos.items)

tos.push("参数类型")
print(tos.items)

tos.push("类型参数名")
print(tos.items)

The above program execution output is:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "参数类型"]
["Swift", "泛型", "参数类型", "类型参数名"]

Where statements

The type of constraint to ensure compliance with the definition of the type of constraint generic function or class.

You can define the parameters of the statement where the constraint in the parameter list.

Where you can write a statement, followed by the type parameter list in the back, where the statement is followed by one or more types of constraints for the association, and (or) the equivalent of one or more types and association types between (equality) relationship.

Examples

The following example defines a generic function named allItemsMatch, used to check whether two instances of Container contains the same elements in the same order.

If all the elements can match, then return to a Boolean value of true, otherwise it is false.

protocol Container {
    typealias ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

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]
    }
}

func allItemsMatch<
    C1: Container, C2: Container
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
    (someContainer: C1, anotherContainer: C2) -> Bool {
        // 检查两个Container的元素个数是否相同
        if someContainer.count != anotherContainer.count {
            return false
        }
        
        // 检查两个Container相应位置的元素彼此是否相等
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        // 匹配所有项,返回 true
        return true
}

var tos = Stack<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
print(tos.items)

tos.push("Where 语句")
print(tos.items)

var eos = ["Swift", "泛型", "Where 语句"]
print(eos)

The above program execution output is:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "Where 语句"]
["Swift", "泛型", "Where 语句"]