iOS/문법

Swift 문법과 친해지기 - 제네릭

HJ39 2023. 1. 6. 01:11

제네릭

타입에 의존하지 않는 범용 코드를 작성할 때 사용

 

제네릭 방법으로 코드를 작성하는 경우

같은 내용의 함수를 오버로딩할 필요 없이 한 번만 작성하여 사용할 수 있다.

 

제네릭 타입

제네릭은 함수뿐만 아니라 구조체, 클래스, 열거형 타입에도 선언하여 사용할 수 있다.

제네릭을 선언할 때 <>를 사용하여 선언한다.

 

제네릭을 이미 우리는 알게 모르게 사용했었다..!

let array: Array<Int> = .init()

swift에서는 배열이 바로 제네릭으로 구현되어 있다!

 

타입 제약

프로토콜 제약

만약 매개변수 2개를 받아서 반환하는 함수를 만들었을 때

func isSameValues<T>(_ a: T, _ b: T) -> Bool {
    return a == b            
}

해당 함수는 오류를 발생시킨다. → 반환할 때 '=='연산자는 Equatable이라는 프로토콜을 준수해야 사용할 수 있는데 아닐 수도 있는 가능성이 있으므로 오류 발생

 

따라서 해당 기능을 사용하고 싶은 경우

func isSameValues<T: Equatable>(_ a: T, _ b: T) -> Bool {
    return a == b
}

T에 Equatable이라는 제약을 줘서 사용할 수 있다.

 

클래스 제약

클래스 제약 같은 경우에도 프로토콜 제약과 유사하지만

 

class Bird{}
class Human{}
class Teacher: Human {}

func printName<T: Human>(_ a:  T) {}

let bird = Bird.init()
let human = Human.init()
let teacher = Teacher.init()

printName(bird)	//오류 발생
printName(human)
printName(teacher)

제네릭에 제약이 부여된 경우 해당 클래스와 연관이 없는 클래스는 사용할 수 없다.

 

제네릭 확장

제네릭으로 구성되어 있는 Array, Class 등을 확장하여 사용할 때 함수 내부를 변경할 수  없지만 where을 이용하여 제약을 부여할 수 있다.

extension Array where Element: FixedWidthInteger {
    mutating func pop() -> Element { return self.removeLast() }
}

위와 같이 where을 이용해 제약을 부여하게 되면 FixedWidthInteger라는 프로토콜을 준수해라 라는 의미이다.

 

제네릭 함수와 오버로딩

func swapValues<T>(_ a: inout T, _ b: inout T) {
    print("generic func")
    let tempA = a
    a = b
    b = tempA
}
 
func swapValues(_ a: inout Int, _ b: inout Int) {
    print("specialized func")
    let tempA = a
    a = b
    b = tempA
}

위와 같이 swapValues라는 제네릭 함수를 오버로딩하여 직접적인 타입을 부여한 후 다른 함수로 변경하여 사용할 수 있다.

(직접적으로 타입을 지정한 함수가 우선순위가 높기 때문!)

 

 

 

 

# 참고한 사이트

  1. https://babbab2.tistory.com/136