Swift의 프로토콜에서 사용자가 정의한 generic 매개변수는 associatedtype 키워드를 통해 처음부터 제공되어 행동의 추상화를 통해 타입화에 대한 더 큰 제어를 제공합니다.
프로토콜이 어떤 타입(예: 컨테이너의 요소 타입)을 정의해야 하는 요구 사항을 선언하면 associatedtype이 없으면 추상화를 가진 프로토콜을 사용할 수 없습니다: 타입이 엄격하게 고정되어야 하며, 일반 코드를 작성할 수 있는 가능성이 사라집니다.
associatedtype은 프로토콜 내에서 관련된 타입을 선언할 수 있게 하여 프로토콜의 구체적인 구현이 무엇인지 정의합니다. 이를 통해 특정 타입을 알 필요 없이 프로토콜 및 generic 함수를 작성할 수 있습니다.
associatedtype을 가진 프로토콜의 예:
protocol MyContainer { associatedtype Item var count: Int { get } mutating func append(_ item: Item) subscript(i: Int) -> Item { get } } struct IntStack: MyContainer { var items = [Int]() mutating func append(_ item: Int) { items.append(item) } var count: Int { items.count } subscript(i: Int) -> Int { items[i] } // Swift는 자동으로 Item = Int임을 추론합니다. }
주요 특징:
let x: MyContainer // 오류),왜 associatedtype이 필요한가요? 이미 Swift에는 generics가 존재하는데.
답변: Generics와 associatedtype은 유사하지만 서로 다른 문제를 해결합니다. Generic 매개변수는 타입이나 함수에 적용되어 일반 구조 및 메서드를 생성할 수 있게 합니다. 그러나 associatedtype은 프로토콜 내부의 추상화로, 구현 타입이 자신의 타입을 정의하라는 요구 사항을 설정합니다. 일반 알고리즘이나 컨테이너에는 generics를 사용하고, 프로토콜을 통해서 동작은 associatedtype을 사용하세요.
associatedtype이 있는 프로토콜을 변수 타입으로 사용할 수 있나요?
아니요, Swift는 associatedtype 정보가 사라지기 때문에 해당 프로토콜을 타입으로 직접 사용할 수 없습니다. 추상화를 원한다면 type erasure를 하거나 generic constraint를 사용할 수 있습니다:
func printElements<C: MyContainer>(container: C) { for i in 0..<container.count { print(container[i]) } }
associatedtype을 "타입"으로 만들려면 어떻게 해야 하나요 (type erasure)?
이런 경우에는 type erasure 패턴을 사용하여 내부 구현을 closure나 box를 통해 저장하는 래퍼를 만듭니다.
struct AnyContainer<T>: MyContainer { private let _append: (T) -> Void private let _count: () -> Int private let _subscript: (Int) -> T ... // 클로저를 통한 초기화 }
개발자가 데이터 모델에 대한 여러 프로토콜을 associatedtype으로 만들었고, 이후 let boxes: [MyContainer]라는 배열을 만들려고 하자, 컴파일러가 오류를 발생시켰습니다. 결국 불필요한 래퍼를 도입하고 타입 안전성을 잃어버려야 했습니다.
장점:
단점:
프로젝트에서 associatedtype이 있는 프로토콜이 컬렉션을 구현하기 위해 사용되었고, 이를 추상화하기 위해 별도의 type erasure 래퍼 AnyCollection이 만들어졌습니다. 이를 통해 ViewModel 레이어에서 컬렉션의 구체적인 구현으로부터 추상화할 수 있었고, 비즈니스 로직 레이어에서 타입 안전성을 잃지 않았습니다.
장점:
단점: