적당한 고통은 희열이다

- 댄 브라운 '다빈치 코드' 중에서

Swift iOS 앱 개발/Swift

[Swift] Generic에 대하여

hongssup_ 2023. 1. 12. 23:39
반응형
Generic은 Swift 의 가장 강력한 기능 중 하나로, Swift 표준 라이브러리의 대부분은 generic 으로 이루어져 있다. 
흔히 사용하고 있는 배열과 딕셔너리가 바로 generic collection으로 제네릭의 대표적인 예이다.
타입을 특정하지 않고 함수나 타입을 구현할 수 있도록 하여, Generic을 사용하면 유연하고 재사용 가능한 코드를 작성할 수 있다.
제네릭을 정의할 때 타입 파라미터로는 placeholder type으로 T를 흔히 사용하며, 사용 시에 타입이 확정되면 실제 타입으로 대체된다.

ex) 제네릭 함수 : swap<T>, 제네릭 타입 : Array<Element>, 커스텀 Stack<Element>

 

Generics

Generic은 어떤 타입이든 넣어줄 수 있는, 유연하고 재사용가능한 함수와 타입을 작성할 수 있도록 해준다. 

중복을 피하고 의도를 명확하고 추상화된 방식으로 표현하는 코드를 작성할 수 있다.  

 

Generic은 Swift 의 가장 강력한 기능 중 하나로, Swift 표준 라이브러리의 대부분은 generic 으로 이루어져 있다. 

대표적인 예로, 흔히 사용하고 있는 배열과 딕셔너리 타입이 generic collection이다.  

Int 형식의 값들을 저장하는 배열을 만들 수도 있고, String 값들을 저장하는 배열을 생성해줄 수도 있어 어떤 타입이든 저장이 가능하다. 

 

Generic Functions

예를들어 파라미터로 받아온 두 인자를 swap 하는 함수를 구현하고 싶을 때, 

타입을 지정하게 되면 타입 별로 swapTwoInts(_ a: inout Int, _ b: inout Int), swapTwoStrings(_ a: inout String, _ b: inout String) 이렇게 함수를 따로 따로 만들어줘야 하지만, generic을 사용하면 swap<T>(_ a: inout T, _ b: inout T) 이런 식으로 한번만 구현해주면 되기 때문에 유연함과 재사용성이 높아진다. 

 

함수를 정의할 때 타입 파라미터로는 placeholder type으로 T를 흔히 사용하고, 함수 사용 시에 타입이 확정되면 실제 타입으로 replace된다. 

 

Generic Types

generic 함수 외에도, Array나 Dictionary 처럼 커스텀 generic 타입을 직접 만들어서 정의해줄 수도 있다.

예를 들어 Stack 을 직접 만들고자 할 때, type 파라미터를 generic으로 만들어 주면 타입에 구애받지 않고 자유롭게 스택을 만들 수 있다. 

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

 

Type Constraints

앞서 이야기한 것 처럼 제네릭 함수나 제네릭 타입은 어떤 타입을 넣어줘도 범용적으로 사용가능하지만, 가끔은 특정 타입 제약 조건을 두는 것이 유용할 때도 있다. Type constraints는 타입 파라미터가 특정 클래스에서 상속하거나 특정 프로토콜을 준수해야 한다고 지정해줄 수 있다. 

예를 들어 Swift의 Dictionary 타입은 key 에 들어갈 수 있는 타입을 hashable로 제한하고 있다. 

 

Associated Types

프로토콜을 정의할 때 associatedtype 키워드를 사용하여 제네릭으로 사용하고자 하는 타입을 placeholder name으로 선언해줄 수도 있다.

 

 


+ Any 와 Generic

전혀 다른 이야기이긴 한데, 스터디 하다가 얘기가 나와서 끄적여봄.

 

Any 타입도 타입에 상관없이 사용이 가능하다고 알고 있지만, 이는 Any 자체가 데이터 타입 중의 하나로 볼 수 있는 범용 타입인 것.

타입을 특정하지 않고 어떤 자료형이든 들어갈 수 있다는 것을 의미하기 때문에 반드시 타입 캐스팅을 해준 후 사용이 가능하다. 

ex) dictionary = [String:Any]() 라고 초기화할 경우, dictionary의 값으로 String 혹은 Int를 다양하게 혼용해서 넣어줄 수도 있다.

 

반면 Generic의 경우, placeholder type <T> 를 타입 파라미터로 사용하여 제네릭 함수를 만들어 사용할 수도 있고, 제네릭 타입 자체를 커스텀으로 직접 만들어 사용할 수도 있다. 사용될 파라미터나 Element의 타입은 특정되어 있지 않아 여러 타입으로 범용적으로 사용이 가능하지만, 해당 제네릭 함수 혹은 타입 내에서 타입의 혼용은 불가능하다.

ex) swap<T>(_ a: inout T, _ b: inout T) 함수의 경우, Int 끼리 혹은 String 끼리는 swap이 가능하지만, Int 와 String을 swap 할 수는 없다. 

728x90
반응형

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

[Swift] defer 란?  (0) 2023.01.20
[Swift] Escape closure 탈출클로저란?  (0) 2023.01.13
[Swift] 고차함수 map, compactMap  (0) 2023.01.11
[Swift] Extension에 대하여  (0) 2023.01.09
[Swift] POP?  (0) 2023.01.07