J
[Swift] associatedtype 본문
1. `associatedtype`이란?
`associatedtype`은 프로토콜 내에서 사용되는 타입의 플레이스홀더를 정의하는 데 사용.
이를 통해 프로토콜을 채택하는 타입이 구체적인 타입을 지정하도록 요구할 수 있음.
`associatedtype`은 프로토콜을 보다 유연하고 재사용 가능하게 만들어 줌.
특히, `Generic`과 함께 사용하면 강력한 타입 추론과 코드 유연성을 제공할 수 있음.
2. `associatedtype` 기본 문법
protocol ProtocolName {
associatedtype TypeName
// 프로토콜 요구 사항
}
- `associatedtype`은 프로토콜의 타입 매개변수와 같은 역할.
- 프로토콜을 채택한 타입은 `associatedtype`으로 선언된 타입을 구현하면서 구체적인 타입으로 정의해야 함.
3. `associatedtype` 사용 예제
protocol Container {
associatedtype Item
var items: [Item] { get set }
mutating func addItem(_ item: Item)
}
struct IntContainer: Container {
var items: [Int] = []
mutating func addItem(_ item: Int) {
items.append(item)
}
}
struct StringContainer: Container {
var items: [String] = []
mutating func addItem(_ item: String) {
items.append(item)
}
}
- `Container` 프로토콜은 `associatedtype Item`을 사용하여 타입 플레이스홀더를 정의.
- `IntContainer`는 `Item`을 `Int`로, `StringContainer`는 `Item`을 `String`으로 정의.
4. `associatedtype`과 `Generic`
`associatedtype`은 `Generic`과 자연스럽게 결합되어 사용할 수 있음.
func printItems<T: Container>(from container: T) {
for item in container.items {
print(item)
}
}
var intContainer = IntContainer()
intContainer.addItem(1)
intContainer.addItem(2)
printItems(from: intContainer) // 1, 2
- 여기서 `printItems` 함수는 `Container` 프로토콜을 준수하는 어떤 타입이든 받아서 동작.
5. `associatedtype`과 `Generic`의 차이점
특징 | `associatedtype` | `Generic` |
사용 위치 | 프로토콜에서 사용 | 함수, 클래스, 구조체, 열거형에서 사용 |
타입 지정 시점 | 프로토콜을 채택하는 타입이 지정 | 함수 호출 시 또는 객체 생성 시 지정 |
주 용도 | 프로토콜의 타입 추론 및 유연성 제공 | 다양한 타입에서 동작하는 코드 작성 |
6. `associatedtype`의 타입 제약
`associatedtype`도 `Generic`처럼 타입 제약(Type Constraints)을 추가할 수 있음.
protocol Container {
associatedtype Item: Equatable
var items: [Item] { get set }
mutating func addItem(_ item: Item)
}
struct IntContainer: Container {
var items: [Int] = []
mutating func addItem(_ item: Int) {
items.append(item)
}
}
- `associatedtype Item`: `Equatable`은 `Item`이 반드시 `Equatable` 프로토콜을 준수하도록 강제.
7. `associatedtype`의 실질적 사용 예
`IteratorProtocol`
Swift의 표준 라이브러리에서 `associatedtype`을 사용하는 대표적인 예는 `IteratorProtocol`.
protocol IteratorProtocol {
associatedtype Element
mutating func next() -> Element?
}
- 이를 준수하는 타입은 `Element`를 구체적인 타입으로 정의해야 함.
struct IntIterator: IteratorProtocol {
var current = 0
mutating func next() -> Int? {
if current < 3 {
defer { current += 1 }
return current
} else {
return nil
}
}
}
var iterator = IntIterator()
while let value = iterator.next() {
print(value) // 0, 1, 2
}
`Sequence`
Swift의 `Sequence`도 `associatedtype`을 사용.
protocol Sequence {
associatedtype Iterator: IteratorProtocol
func makeIterator() -> Iterator
}
- `makeIterator` 메서드는 `IteratorProtocol`을 준수하는 타입을 반환해야 함.
struct Countdown: Sequence {
var start: Int
func makeIterator() -> CountdownIterator {
return CountdownIterator(current: start)
}
}
struct CountdownIterator: IteratorProtocol {
var current: Int
mutating func next() -> Int? {
if current > 0 {
defer { current -= 1 }
return current
} else {
return nil
}
}
}
let countdown = Countdown(start: 3)
for number in countdown {
print(number) // 3, 2, 1
}
8. 언제 `associatedtype`을 사용할까?
- 프로토콜이 타입에 의존적인 동작을 정의할 때.
- `Generic`처럼 다양한 타입에서 동작하지만, 구체적인 타입 정의는 프로토콜을 채택하는 타입에 맡기고 싶을 때.
- 프로토콜 요구 사항이 연관 타입 간의 관계를 포함할 때.
9. 결론
Swift의 `associatedtype`은 프로토콜의 유연성을 극대화하는 강력한 도구.
이를 활용하면 특정 타입에 의존하지 않는 일반화된 프로토콜을 정의할 수 있음.
특히, `Generic`과 결합하면 코드의 재사용성을 더욱 높일 수 있음.
그러나 지나치게 복잡한 타입 의존성을 도입하지 않도록 적절히 사용하는 것이 중요.
'Swift' 카테고리의 다른 글
[Swift] weak, unowned (0) | 2024.04.18 |
---|---|
[Swift] self / Self (0) | 2024.04.18 |
[Swift] Protocol (0) | 2024.04.18 |
[Swift] Generic (0) | 2024.04.15 |
[Swift] GCD vs Swift Concurrency: 스레드 관리와 성능 (0) | 2024.04.15 |