스위프트 제네릭


Swift는 유연하고 재사용 가능한 함수와 유형을 작성할 수 있는 제네릭을 제공합니다.

Swift 표준 라이브러리는 일반 코드로 구축되었습니다.

Swift의 배열 및 사전 유형은 모두 일반 세트입니다.

Int 배열, String 배열 또는 기타 Swift 유형 데이터 배열을 만들 수 있습니다.

다음 예는 두 개의 Int 값을 교환하는 데 사용되는 비일반 함수 교환입니다.

// 定义一个交换两个变量的函数
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)")

위 프로그램 실행의 출력 결과는 다음과 같습니다.

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

일반 함수는 Int 또는 String과 같은 모든 유형에 액세스할 수 있습니다.

다음 예는 두 개의 Int 및 String 값을 교환하는 데 사용되는 일반 함수 교환입니다.

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)")

위 프로그램 실행의 출력 결과는 다음과 같습니다.

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

이 함수의 일반 버전은 자리 표시자 유형 이름을 사용합니다(보통 다음과 같습니다). 실제 유형 이름(예: Int, String 또는 Double) 대신 문자 T)로 표시됩니다. 자리표시자 유형 이름은 T 유형이 무엇인지 암시하지 않지만, T 유형이 무엇을 나타내는지에 관계없이 a와 b가 동일한 T 유형이어야 함을 암시합니다. exchange(_:_:) 함수가 호출될 때마다 전달되는 실제 유형만이 T가 나타내는 유형을 결정할 수 있습니다.

또 다른 차이점은 자리 표시자 유형 이름(T) 뒤에 이 일반 함수 이름이 꺾쇠 괄호로 묶여 있다는 것입니다(


Generic 유형

Swift를 사용하면 자신만의 일반 유형을 정의할 수 있습니다.

사용자 정의 클래스, 구조 및 열거형 배열 및 사전의 사용법과 마찬가지로 모든 유형에서 작동합니다.

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()

위 프로그램 실행의 출력 결과는 다음과 같습니다.

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

확장 일반 유형

일반 유형을 확장하면(확장 키워드 사용) 확장 정의에 유형 매개변수 목록을 제공할 필요가 없습니다. 더 편리하게는 원래 유형 정의에 선언된 유형 매개변수 목록을 확장에서 사용할 수 있으며 이러한 매개변수 이름은 참조로 사용됩니다.

예제

다음 예에서는 first라는 이름의 읽기 전용 계산 속성을 추가하여 일반 TOS 유형을 확장합니다. 이는 스택에서 제거하지 않고 최상위 요소를 반환합니다.

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)")
}

위 프로그램 실행의 출력 결과는 다음과 같습니다.

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

유형 제약 조건

유형 제약 조건은 지정된 클래스에서 상속되거나 특정 프로토콜 또는 프로토콜 구성 요소를 따라야 하는 유형 매개 변수를 지정합니다.

유형 제약 조건 구문

제네릭 함수에 대해 작동하는 유형 매개변수 체인의 일부로 유형 매개변수 이름 뒤에 콜론으로 구분된 유형 제약조건을 작성할 수 있습니다. 제약조건의 기본 구문은 다음과 같습니다(제네릭 유형의 구문과 동일). :

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

Instance

// 函数可以作用于查找一字符串数组中的某个字符串
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)")
}

위 프로그램 실행의 출력은 다음과 같습니다.

llama 的下标索引值为 2

Associated type instance

Swift에서 typealias 키워드를 사용하여 연관 유형을 설정합니다.

프로토콜을 정의할 때 다음이 유용할 때가 있습니다. 하나 이상의 관련 유형을 프로토콜 정의의 일부로 선언합니다.

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)

위 프로그램 실행의 출력은 다음과 같습니다.

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

여기서 구문

은 유형이 일반 함수 또는 클래스의 정의 제약 조건을 준수하는지 확인할 수 있습니다.

.

매개변수 목록의 where 문을 통해 매개변수 제약 조건을 정의할 수 있습니다.

유형 매개변수 목록 바로 뒤에 where 문을 작성할 수 있으며, 그 뒤에는 연관된 유형에 대한 하나 이상의 제약 조건 및/또는 유형과 연관된 유형 관계 사이의 하나 이상의 동등성이 뒤따릅니다.

Example

다음 예에서는 두 개의 컨테이너 인스턴스에 동일한 요소가 동일한 순서로 포함되어 있는지 확인하기 위해 allItemsMatch라는 일반 함수를 정의합니다.

모든 요소가 일치할 수 있으면 true라는 부울 값이 반환되고, 그렇지 않으면 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)

위 프로그램 실행의 출력 결과는 다음과 같습니다.

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