Danny의 iOS 컨닝페이퍼
article thumbnail

Associated Types (관련 타입)

자 일단 공식문서 부터 보고 가시죠

When defining a protocol, it’s sometimes useful to declare one or more associated types as part of the protocol’s definition. An associated type gives a placeholder name to a type that’s used as part of the protocol. The actual type to use for that associated type isn’t specified until the protocol is adopted. Associated types are specified with the associatedtype keyword.

Associated TypeGeneric에서 나오는 개념 중 하나입니다.

하나 이상의 관련 타입을 선언할 때 유용하며, Generic과 같이 일종의 Place holder 역할을 해준다고 합니다.

즉, 진짜 타입을 주는 게 아니라 타입의 샘플을 주는 거죠. 사용할 실제 타입은 프로토콜이 채택될 때까지 지정되지 않으니 재사용성이 높아집니다.

 

즉, Associated Type은 일종에 프로토콜을 위한 제네릭 문법이라고 생각하면 되겠습니다.

 

 

정의

Associated Type는 타입으로 사용됩니다. 타입이므로 첫 번째 단어는 대문자가 되겠죠?

이렇게, 일단 사용되기 전까지 SomeType은 어떠한 타입도 될 수 있습니다.

protocol SomeProtocol {
    associatedtype SomeType
}

 

 

구현 예시

이렇게 만들어진 DannyProtocol을 채택하게 되면, 반드시 age를 String 타입으로 정의해줘야 합니다.

protocol DannyProtocol {
    var age: String { get }
}

그런데 여기서 만약, 다른 타입을 사용할 여지가 있거나 사용하고 싶을 때, 사용하는 것이 바로 associatedType 입니다.

 

 

한번, 간단히 사용해 봅시다.

 

먼저 MyType으로 Place holder 역할을 하는 타입을 만들어 줍니다.

associatedTypePlace holder 역할만 할 뿐, 사용되기 전까지 값을 갖지 않는다고 했죠? 넣어주세요!

protocol DannyProtocol {
    associatedtype MyType
    var age: MyType { get }
}

 

그럼 이렇게 Associated Type을 만들어 줬고, 한번 만든 프로토콜을 사용 볼까요?

 

이와 같이 채택한 곳에서 원하는 타입으로 정할 수 있습니다.

struct Danny: DannyProtocol {
    typealias MyType = Int
    var age = 10
}
struct Danny: DannyProtocol {
    typealias MyType = String
    var age = "열 살"
}

 

또한 타입 추론으로 typealias를 생략해 줘도 무관하죠.

하지만, 되도록이면 아래와 같이 타입은 명시적으로 나타내줍시다.

struct Danny: DannyProtocol {
    var age: Int = 10
}

 

 

특징

associatedType타입이라고 하였습니다.

 

즉, 다른 프로토콜을 채택하여 제약조건을 줄 수 있죠.

대신 사용 시 채택한 MyType이 사용할 프로토콜을 준수하고 있어야 합니다.

 

예시로 Equatable를 사용해 봅시다.

Equatable 대한 정보를 알고 싶다면 프로토콜 Equatable (타입 비교) 글을 참고해 주세요.

protocol DannyProtocol {
    associatedtype MyType: Equatable
    var value: MyType { get }
}

struct Danny: DannyProtocol {
    var age: Int
    
    static func == (lhs: Self, rhs: Self) -> Bool {
          return lhs.age == rhs.age
      }
}
let youngDanny = Danny(age: 10)
let oldDanny = Danny(age: 30)

youngDanny == oldDanny
// false

이렇게 Equatable을 준수하는 프로토콜을 만들어 줬습니다.

 

예제가 조금 억지스럽긴 한데, 요점은 다른 프로토콜 채택이 가능하다.

즉, 원하는 제약 조건을 줄 수 있다는 것입니다.

 

 

Generic과 같이 활용하기

이제 구조체나 클래스와 같은 커스텀 타입에서 associatedType을 통해 프로토콜채택할 때마다,

원하는 타입으로 만들어 사용할 수 있게 됐습니다.

 

하지만 예제와 같이 다른 타입 인스턴스만들려면 구조체를 한 개 더 만들어야 하는 수고가 발생하게 됩니다...

struct DannyInt: DannyProtocol {
    typealias MyType = Int
    var age = 10
}
struct DannyStr: DannyProtocol {
    typealias MyType = String
    var age = "열 살"
}

 

이 때는 바로 Generic을 활용해서 만들면 됩니다.

struct Danny<T>: DannyProtocol {
    var age: T
}

위에 설명했듯이 타입을 정해주면 따로 typealias 설정은 생략할 수 있다고 했죠.

Generic은 일종에 타입의 샘플로 플레이스홀더 블라블라...

 

이렇게 Generic을 활용하면 인스턴스를 생성 시 다른 타입으로 생성이 가능하게 되죠!

let dannyInt = Danny(age: 10)
let DannyStr = Danny(age: "열 살")

 

 

부족한 설명이지만, 조금은 이해 가셨나요?

틀린 내용이 있다면 언제든지 지적해 주시면 감사히 받겠습니다. 🫠
읽어주셔서 감사합니다 😃

 

반응형
profile

Danny의 iOS 컨닝페이퍼

@Danny's iOS

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!