본문 바로가기
iOS/Swift

[Swift] Generics 제네릭

by 최지철 2023. 12. 13.
728x90
반응형

Generics

  • 타입에 의존하지 않는 범용코드를 작성할 때, 사용
  • 제네릭을 사용하면, 중복을 피하고 코드를 유연하게 작성할 수 있다.
  • Swift의 강력한 특징 중 하나이고 Swift 표준 라이브러리 대부분은 제너릭 코드로 되어 있다
    • 예를 들어 Swift의 Array  Dictionary 타입은 둘다 제너릭 콜렉션
    • Int 값을 가진 배열, 또는 String 값을 가진 배열 또는 실제로 Swift에서 생성될 수 있는 다른 모든 타입에 대한 배열을 생성 가능
    • 모든 지정된 타입의 값을 저장하기 위한 딕져너리를 생성할 수 있고 해당 타입에 대한 제한은 없다.

 

Generic Function

  • 제너릭이 해결하는 문제
    • 아래와 같이 일반적으로 a  b 가 같은 타입이 아니면 바꾸는 것은 불가능하다. Swift는 타입 안정성 언어이고 String 타입의 변수와 Double 타입의 변수가 서로 값을 바꾸도록 허락하지 않으며, 이러한 시도는 컴파일 에러가 발생한다.
func swapTwoStrings(_ a: inout String, _ b: inout String) {
    let temporaryA = a
    a = b
    b = temporaryA
}

func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
    let temporaryA = a
    a = b
    b = temporaryA
}

 

  • 제너릭 함수 (Generic functions) 는 모든 타입과 함께 동작할 수 있다.
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
  • Int, String, 또는 Double 와 같은 실제 타입 이름 대신에 이 경우 T 라는 임의의 타입 이름을 사용한다. 이 임의의 타입 이름은 T 가 무엇이어야 하는지 아무 말도 하지 않지만 T 가 무엇을 나타내든 a  b 는 모두 같은 타입 T여야 한다고 말한다. T 의 실제 타입은 swapTwoValues(_:_:) 함수가 호출될 때마다 결정된다.
  • 첫번째 인자가 Int면 두번째 인자도 Int기 때문에 같은 인자의 타입 값을 넣어야한다.
  • 제너릭 함수와 제너릭이 아닌 함수 사이의 다른 차이점
    • 제너릭 함수의 이름 (swapTwoValues(_:_:))에 바로 임의의 타입 이름 (T)이 꺾쇠 괄호 내 (<T>)에 위치한다는 것 이다. 이 괄호는 T  swapTwoValues(_:_:) 함수 정의 내에서 임의의 타입 이름이라고 Swift에게 말한다. T 는 임의의 타입이므로 Swift는 T 라는 실제 타입을 찾지 않는다.
타입 파라미터 (Type Parameters)
  • 예제에서 임의의 타입 T  타입 파라미터 (type parameter) 의 예이다.
  • 타입 파라미터는 임의의 타입을 지정하고 이름을 지정하며 꺾쇠 괄호 (예: <T>) 사이에 기록하고 함수의 이름 바로 뒤에 작성
  • 콤마로 구분된 꺾쇠 괄호 안에 여러개 타입 파라미터를 작성하여 하나 이상의 타입 파라미터를 제공할 수 있다.
  • 값이 아니라 타입에 대한 임의의 표시라는 것을 나타내기 위해 항상 타입 파라미터가 주어질 때 대문자 이름 (T MyTypeParameter 와 같은)으로 주어진다.

 

Generic Types

  • 제너릭 함수 외에도 Swift는 고유한 제너릭 타입 (generic types) 을 정의할 수 있다.
  • Array  Dictionary 와 유사한 방식으로 모든 타입 에서 동작할 수 있는 사용자 정의 클래스, 구조체, 그리고 열거형이다.
struct Stack<T> {
    let items: [T] = []
 
    mutating func push(_ item: T) { ... }
    mutating func pop() -> T { ... }
}
  • 제네릭 타입 객체 생성 시,
let stack1: Stack<Int> = .init()
let stack2 = Stack<Int>.init()
 

Type Constraints

  • 제네릭 함수와 타입을 사용할 때, 특정 클래스의 하위 클래스나, 특정 프로토콜을 준수하는 타입만 받을 수 있게 제약 할 수 있다.

프로토콜 제약

func isSameValues<T: Equatable>(_ a: T, _ b: T) -> Bool {
    return a == b               
}
  • Equatable 프로토콜을 준수하는 파라미터만 받을 수 있음

 

클래스 제약

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

 

 

오버로딩

  • 제네릭 함수도 오버로딩이 가능하다. 만약 오버로딩시, 타입이 지정되어있는 경우, 타입이 지정된 함수가 우선순위가 더 높다
728x90
반응형

'iOS > Swift' 카테고리의 다른 글

[Swift] designated init 과 convenience init  (0) 2024.05.30
[Swift] inout 파라미터  (0) 2024.05.28
[Swift] Concurrency [2]  (0) 2023.10.25
[Swift] Concurrency [1]  (1) 2023.10.23
[Swift] Optional  (1) 2023.10.17