수색…


비고

일반 코드를 사용하면 사용자가 정의한 요구 사항에 따라 모든 유형에서 작동 할 수있는 유연하고 재사용 가능한 함수 및 유형을 작성할 수 있습니다. 중복을 피하고 그 의도를 명확하고 추상적으로 표현하는 코드를 작성할 수 있습니다.

Generics는 Swift의 가장 강력한 기능 중 하나이며 Swift 표준 라이브러리의 대부분은 일반적인 코드로 작성됩니다. 예를 들어 Swift의 Array 및 Dictionary 유형은 모두 일반적인 컬렉션입니다. Int 값을 포함하는 배열 또는 String 값을 보유하는 배열을 만들거나 Swift에서 만들 수있는 다른 유형의 배열을 만들 수 있습니다. 마찬가지로 지정된 유형의 값을 저장하기 위해 사전을 만들 수 있으며 그 유형에 대한 제한이 없습니다.

출처 : Apple의 신속한 프로그래밍 언어

일반 자리 표시 자 유형 제한

generic 클래스의 형식 매개 변수가 프로토콜 을 구현하도록 할 수 있습니다 (예 : Equatable

class MyGenericClass<Type: Equatable>{
    
    var value: Type
    init(value: Type){
        self.value = value
    }
    
    func getValue() -> Type{
        return self.value
    }

    func valueEquals(anotherValue: Type) -> Bool{
        return self.value == anotherValue
    }
}

MyGenericClass 새로 만들 때마다 type 매개 변수는 Equatable 프로토콜을 구현 Equatable 합니다 (type 매개 변수가 == 사용하여 같은 유형의 다른 변수와 비교 될 수 있는지 확인)

let myFloatGeneric = MyGenericClass<Double>(value: 2.71828) // valid
let myStringGeneric = MyGenericClass<String>(value: "My String") // valid

// "Type [Int] does not conform to protocol 'Equatable'"
let myInvalidGeneric = MyGenericClass<[Int]>(value: [2]) 

let myIntGeneric = MyGenericClass<Int>(value: 72)
print(myIntGeneric.valueEquals(72)) // true
print(myIntGeneric.valueEquals(-274)) // false

// "Cannot convert value of type 'String' to expected argument type 'Int'"
print(myIntGeneric.valueEquals("My String"))

제네릭의 기본

제네릭 은 형식에 대한 자리 표시 자이므로 여러 유형에 적용 할 수있는 유연한 코드를 작성할 수 있습니다. Any 에 대해 generics를 사용하면 컴파일러가 강력한 유형 안전을 적용 할 수 있다는 장점이 있습니다.

일반적인 자리 표시자는 꺾쇠 괄호 <> 안에 정의됩니다.

일반 함수

함수 의 경우이 자리 표시자는 함수 이름 뒤에 위치합니다.

/// Picks one of the inputs at random, and returns it
func pickRandom<T>(_ a:T, _ b:T) -> T {
    return arc4random_uniform(2) == 0 ? a : b
}

이 경우 제네릭 자리 표시자는 T 입니다. 함수를 호출 할 때 Swift는 T 의 유형을 추측 할 수 있습니다 (단순히 실제 유형의 자리 표시 자 역할을 함).

let randomOutput = pickRandom(5, 7) // returns an Int (that's either 5 or 7)

함수에 두 개의 정수를 전달합니다. 따라서 Swift는 T == Int 유추합니다. 따라서 함수 서명은 (Int, Int) -> Int 로 추정됩니다.

제네릭이 제공하는 강력한 유형 안전성 때문에 함수의 인수와 반환은 모두 같은 유형이어야합니다. 따라서 다음은 컴파일되지 않습니다.

struct Foo {}

let foo = Foo()

let randomOutput = pickRandom(foo, 5) // error: cannot convert value of type 'Int' to expected argument type 'Foo'

일반 유형

클래스 , 구조체 또는 enum 과 함께 제네릭을 사용하려면 형식 이름 뒤에 일반 자리 표시자를 정의 할 수 있습니다.

class Bar<T> {
    var baz : T
    
    init(baz:T) {
        self.baz = baz
    }
}

이 일반 placeholder는 클래스 Bar 를 사용할 때 타입을 필요로합니다. 이 경우 이니셜 라이저 init(baz:T) 에서 유추 할 수 있습니다.

let bar = Bar(baz: "a string") // bar's type is Bar<String>

여기서 일반 placeholder T 는 String 유형으로 유추되어 Bar<String> 인스턴스를 만듭니다. 유형을 명시 적으로 지정할 수도 있습니다.

let bar = Bar<String>(baz: "a string")

유형과 함께 사용하면 주어진 일반 placeholder는 주어진 인스턴스의 전체 수명 동안 해당 유형을 유지하며 초기화 후에는 변경할 수 없습니다. 따라서 baz 속성에 액세스 할 때이 인스턴스에 대해 항상 String 유형이됩니다.

let str = bar.baz // of type String

일반적인 유형 전달

제네릭 형식을 전달할 때 대부분의 경우 예상되는 일반적인 자리 표시 자 유형에 대해 명시해야합니다. 예를 들어, 함수 입력으로 :

func takeABarInt(bar:Bar<Int>) {
    ...
}

이 함수는 Bar<Int> 만 허용합니다. 제네릭 자리 표시 자 유형이 Int 가 아닌 Bar 인스턴스를 전달하려고하면 컴파일러 오류가 발생합니다.

일반 자리 표시 자 이름 지정

일반적인 자리 표시 자 이름은 한 글자로만 제한되지 않습니다. 주어진 자리 표시자가 의미있는 개념을 나타내는 경우 설명이 포함 된 이름을 지정해야합니다. 예를 들어, 스위프트의 Array 이라는 일반적인 틀이 Element 특정의 요소 형을 정의, Array 인스턴스를.

public struct Array<Element> : RandomAccessCollection, MutableCollection {
    ...
}

일반 클래스 예제

Type 파라미터의 Type 를 가지는 범용 클래스

class MyGenericClass<Type>{
 
    var value: Type
    init(value: Type){
        self.value = value
    }
 
    func getValue() -> Type{
        return self.value
    }
 
    func setValue(value: Type){
        self.value = value
    }
}

이제 type 매개 변수를 사용하여 새 객체를 만들 수 있습니다.

let myStringGeneric = MyGenericClass<String>(value: "My String Value")
let myIntGeneric = MyGenericClass<Int>(value: 42)
 
print(myStringGeneric.getValue()) // "My String Value"
print(myIntGeneric.getValue()) // 42
 
myStringGeneric.setValue("Another String")
myIntGeneric.setValue(1024)
 
print(myStringGeneric.getValue()) // "Another String"
print(myIntGeneric.getValue()) // 1024

여러 유형 매개 변수로 제네릭을 만들 수도 있습니다

class AnotherGenericClass<TypeOne, TypeTwo, TypeThree>{
 
    var value1: TypeOne
    var value2: TypeTwo
    var value3: TypeThree
    init(value1: TypeOne, value2: TypeTwo, value3: TypeThree){
        self.value1 = value1
        self.value2 = value2
        self.value3 = value3
    }
 
    func getValueOne() -> TypeOne{return self.value1}
    func getValueTwo() -> TypeTwo{return self.value2}
    func getValueThree() -> TypeThree{return self.value3}
}

그리고 같은 방식으로 사용됩니다.

let myGeneric = AnotherGenericClass<String, Int, Double>(value1: "Value of pi", value2: 3, value3: 3.14159)
 
print(myGeneric.getValueOne() is String) // true
print(myGeneric.getValueTwo() is Int) // true
print(myGeneric.getValueThree() is Double) // true
print(myGeneric.getValueTwo() is String) // false
 
print(myGeneric.getValueOne()) // "Value of pi"
print(myGeneric.getValueTwo()) // 3
print(myGeneric.getValueThree()) // 3.14159

일반 클래스 상속

일반 클래스는 상속 될 수 있습니다.

// Models
class MyFirstModel {
}

class MySecondModel: MyFirstModel {
}

// Generic classes
class MyFirstGenericClass<T: MyFirstModel> {
    
    func doSomethingWithModel(model: T) {
        // Do something here
    }
    
}

class MySecondGenericClass<T: MySecondModel>: MyFirstGenericClass<T> {
    
    override func doSomethingWithModel(model: T) {
        super.doSomethingWithModel(model)
        
        // Do more things here
    }
    
}

Generics를 사용하여 배열 함수 단순화

객체 지향 remove 함수를 작성하여 배열의 기능을 확장하는 함수입니다.

// Need to restrict the extension to elements that can be compared.
// The `Element` is the generics name defined by Array for its item types.
// This restriction also gives us access to `index(of:_)` which is also
// defined in an Array extension with `where Element: Equatable`.
public extension Array where Element: Equatable {
    /// Removes the given object from the array.
    mutating func remove(_ element: Element) {
        if let index = self.index(of: element ) {
            self.remove(at: index)
        } else {
            fatalError("Removal error, no such element:\"\(element)\" in array.\n")
        }
    }
}

용법

var myArray = [1,2,3]
print(myArray)

// Prints [1,2,3]

이 함수를 사용하여 색인이 필요없는 요소를 제거하십시오. 그냥 제거 할 개체를 전달하십시오.

myArray.remove(2)
print(myArray)

// Prints [1,3]

유형 안전성 향상을 위해 제네릭 사용

제네릭을 사용하지 않고이 예제를 사용 해보자.

protocol JSONDecodable {
    static func from(_ json: [String: Any]) -> Any?
}

프로토콜 선언은 실제로 사용하지 않는 한 괜찮아 보입니다.

let myTestObject = TestObject.from(myJson) as? TestObject

왜 결과를 TestObject 로 캐스팅해야합니까? 프로토콜 선언의 Any 반환 유형 때문입니다.

generics를 사용하면 런타임 오류를 유발할 수있는이 문제를 피할 수 있습니다.

protocol JSONDecodable {
    associatedtype Element 
    static func from(_ json: [String: Any]) -> Element?
}

struct TestObject: JSONDecodable {
    static func from(_ json: [String: Any]) -> TestObject? {
    }
}

let testObject = TestObject.from(myJson) // testObject is now automatically `TestObject?`

고급 형식 제약 조건

where 절을 사용하여 제네릭에 대한 여러 유형 제약 조건을 지정할 수 있습니다.

func doSomething<T where T: Comparable, T: Hashable>(first: T, second: T) {
    // Access hashable function
    guard first.hashValue == second.hashValue else {
        return
    }
    // Access comparable function
    if first == second {
        print("\(first) and \(second) are equal.")
    }
}

인수 목록 다음에 where 절을 작성하는 것도 유효합니다.

func doSomething<T>(first: T, second: T) where T: Comparable, T: Hashable {
    // Access hashable function
    guard first.hashValue == second.hashValue else {
        return
    }
    // Access comparable function
    if first == second {
        print("\(first) and \(second) are equal.")
    }
}

확장은 조건을 만족하는 유형으로 제한 될 수 있습니다. 함수는 유형 조건을 만족하는 인스턴스에서만 사용할 수 있습니다.

// "Element" is the generics type defined by "Array". For this example, we
// want to add a function that requires that "Element" can be compared, that
// is: it needs to adhere to the Equatable protocol.
public extension Array where Element: Equatable {
    /// Removes the given object from the array.
    mutating func remove(_ element: Element) {
        // We could also use "self.index(of: element)" here, as "index(of:_)"
        // is also defined in an extension with "where Element: Equatable".
        // For the sake of this example, explicitly make use of the Equatable.
        if let index = self.index(where: { $0 == element }) {
            self.remove(at: index)
        } else {
            fatalError("Removal error, no such element:\"\(element)\" in array.\n")
        }
    }
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow