안녕하세요.
이번엔 Swift의 opaque types와 boxed protocol types 관련 Document를 읽고 정리해 보는 시간을 가져볼게요.
(Document에 있는 예제를 활용해서 정리해 볼게요.)
Shape이란 protocol을 정의하고, Shape protocol을 준수하는 Triangle이란 구조체가 있다고 해볼게요.
여기까진 ㅇㅋ.
제네릭을 사용해서 FlippedShape란 구조체도 만들어봤어요.
이것도 ㅇㅋ... 라고 넘길 수도 있지만!
flippedTriangle 상수를 잘 살펴보면 제네릭의 구체적인 타입까지 같이 노출되고 있죠?
만약 제네릭 타입을 2개 받는 구조라면, 2개의 구체적인 타입까지 같이 노출되게 됩니다.
사실 사용자 입장에선 제네릭 타입이 어떤 타입인지 알 필요가 없습니다. (알면 안 돼서는 정보가 있을 수도 있구요.)
이때, opaque type 또는 boxed protocol type을 사용하면 타입을 숨기는 것(=추상화)이 가능해집니다.
# Opaque type
Opaque type은 Generic type과 개념이 반대라고 생각하시면 됩니다.
아래 코드를 볼까요?
Generic type을 반환하는 경우, 함수를 호출한 곳에서 Generic type의 구체적인 타입을 알기 때문에 반환값의 타입도 알 수 있으며 함수 내부에서는 추상화된 상태에서 로직을 구현해야 합니다.
Opaque type을 반환하는 경우, 추상화를 해서 반환하기 때문에 함수를 호출한 곳에서 어떤 타입인지 알 수 없습니다.
protocol 앞에 'some' 이란 키워드를 붙여주는 것으로 Opaqueu type으로 선언할 수 있습니다.
아래는 Opaque type을 반환하는 예시입니다.
(makeTrapezoid 함수 안에선 구체적인 타입을 자유롭게 사용했으면서도 반환값은 'Shape protocol을 준수하는 어떤 타입'으로 추상화해주고 있는 것을 볼 수 있습니다.)
Opaque type을 사용할 땐 주의해야 할 점이 있어요.
Opaque type은 타입 식별자를 보존합니다.
그 말은 즉, Opaque type으로 선언된 변수 또는 Opaque type을 반환하는 함수에서는 특정 프로토콜을 준수한다면 어떤 타입이든 상관없지만 underlying type은 1가지만 될 수 있다는 의미입니다.
[참고]
underlying type은 Opaque type으로 추상화 되기 전의 구체적인 타입을 의미합니다.
underlying type이 1가지만 될 수 있기 때문에, 컴파일러가 underlying type이 뭔지 추론이 가능합니다.
이게 말로 설명하자니까 좀 어려운데요, 아래 예시를 보면 쉽게 이해되실 거에요.
분기처리로 어쩔 땐 Square 타입을 반환하고 어쩔 땐 FlippedShape 타입을 반환하게 되면, 경우에 따라 underlying type이 달라질 수 있기 때문에 컴파일 에러가 발생합니다.
opaque type을 Item으로 가지는 Collection을 반환할 때도 마찬가지에요. 해당 Collection 안에는 동일한 특정 타입만 Item으로 가질 수 있어요.
(Collection 안에 여러 가지 타입이 같이 있을 수 없어요.)
변수로 Opaque type을 사용하는 경우에도 같은 개념이 적용되어서 에러가 발생합니다.
# Boxed Protocol Types
boxed protocol type은 existential type이라고도 합니다.
(existential type에 대해서 여기에 짧게 설명해둔 것이 있으니 참고해 주세요.)
boxed protocol type으로 선언하는 방법은 protocol 앞에 'any' 키워드를 붙여주면 됩니다.
[참고]
any 키워드를 붙이면 'explicit existential type', any 키워드를 사용하지 않으면 'existential type'이라고 부릅니다.
explicit existential type과 existential type 모두 boxed protocol type 이라고 퉁쳐서 부르는 것 같아요!
Boxed protocol type은 타입 식별자를 보존하지 않습니다.
그 말은 즉, Boxed protocol type으로 선언된 변수 또는 Opaque type을 반환하는 함수에서는 특정 프로토콜을 준수한다면 어떤 타입이 되는 상관 없습니다.
타입 식별자를 보존하지 않기 때문에, 언제든지 구체적인 타입이 바뀔 수 있고 구체적인 타입이 뭔지는 런타임 때 알 수 있습니다.
아래 예시처럼, [any Shape]로 선언할 경우 Shape 프로토콜을 준수한 여러 타입이 들어갈 수 있습니다.
# Opaque type과 Boxed protocol type의 차이점
가장 큰 차이점은 타입 식별자를 보존 여부입니다.
Opaque type을 반환하는 함수는 실질적으로 반환하는 underlying type은 1가지 이지만, boxed protocol type을 반환할 경우 실제 타입은 여러가지가 될 수 있는 것이죠.
Opaque type은 타입 식별자를 보존하기 때문에 추상화되어 있음에도 불구하고 컴파일 타임 때 타입 추론이 가능하기 때문에 == 비교를 할 수 있습니다.
(반대로 boxed protocol type은 런타임 때 타입이 결정되므로 컴파일 타임 때 어떤 타입인지를 모르니 == 비교를 할 수 없습니다.)
아래처럼 Shape 프토토콜에 == 연산자를 추가해보면
opaque type 변수끼리는 == 비교를 할 수 있지만, boxed protocol type 변수끼리 == 비교를 할 경우 에러가 발생합니다.
# 요약
- opaque는 '오페잌' 이라고 읽는다.
- opaque type은 프로토콜 앞에 'some' 키워드를, boxed protocol type은 'any' 키워드를 붙인다.
- boxed protocol type은 existential type이라고도 부른다.
- opaque type은 타입 식별자를 보존한다. 추상화가 됐을 뿐 underlying type은 1가지만 가질 수 있다.
- boxed protocol type은 타입 식별자를 보존한다. 실질적인 타입은 언제든지 바뀔 수 있다.
- opaque type은 == 비교를 할 수 있고 boxed protocol type은 == 비교를 할 수 없다.
# 참고
- https://docs.swift.org/swift-book/documentation/the-swift-programming-language/opaquetypes
- https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md
이번 글은 여기서 마무리.
'Swift' 카테고리의 다른 글
@testable (0) | 2023.11.21 |
---|---|
Dictionary Enum.rawValue subscript (0) | 2023.08.08 |
Thread.sleep vs Task.sleep (0) | 2023.06.01 |
[Swift 5.8] weak self 사용 시 self 생략 가능해짐 (0) | 2023.05.15 |
KeychainAccess Array 저장 API 추가하기 (0) | 2023.04.20 |