티스토리 뷰

iOS/Swift-Memo

Swift6의 Protocol (Existential any)

malrang-malrang 2022. 7. 24. 18:25

Protocol과 Generic 의 Swift6 (Existential any)

이번글은 업데이트될 Swift6 를 위해 공부한 자료입니다.

Existential Type 이란?

직역 하자면 실존 타입 으로 우리가 게속 사용했던 개념이다.
Swift Documents- Protocol 문서의 Protocols as Types 파트 도입부를 읽어보면 다음과같이 설명되어있다.

Using a protocol as a type is sometimes called an existential type
프로토콜을 타입으로 사용하는 것은 existential type 이라고 불리기도한다.

아래의 예시 코드는 existential type 을 활용한 예시 코드.

protocol Pet {
  func eat()
}

struct Cat: Pet {
  var name: String
  func eat()
}

위의 예시 코드에서 Cat은 concreate type이라 한다.

var somePet: Pet = Cat(name: "malrang")

위의 코드에서 Pet이라는 프로토콜 타입을 Existential Type이라고 한다.

func feed(to pet: Pet) {
    pet.eat()
}

요런식으로 메서드를 만들어서 우리는 아래와 같이 사용해왔다.

let cat: Pet = Cat(name: "malrang")
feed(to: cat)

하지만 이렇게 사용하게되면 dynamic dispatch를 사용하게되어 비용이 많이 들게된다.

위의 코드가 컴파일 될 때 Swift는 어떤 타입의 eat()이 호출될지 모르기 때문이다.

cat인스턴스는 Cat타입이 아니라 Pet타입(existential type)으로 정의 되어있기 때문이며 컴파일 시점에 어떤 타입의 eat()을 호출해야할지 알수 없게되어 런타임에 알수있게 된다.

어떤 타입의 eat()을 호출해야하는지 알기위해 Swift는 extential container 라는것도 만들고 Heap도 사용하게 되어 비용이 많이 든다.

이러한 문제를 해결하기위해 제네릭을 활용하면 성능을 개선 시킬수 있게된다.

Protocol + Generic 을 사용한 예시코드

struct Cat: Pet {
    let name: String
    func eat() {}
}
func feed<T: Pet>(to pet: T) {
    pet.eat
}

아까의 예시코드와 다르게 제네릭 을사용하여 함수를 정의해주었다.
Pet이라는 프로토콜을 준수하는 콘크리트 타입만 파라미터로 받을수 있도록 했다.

let cat = Cat(name: "malrang")
feed(to: cat)

위의 코드가 컴파일될 때 Swift는 다음과 같이 생각한다.

  1. Cat 인스턴스를 만들고
  2. Cat 타입을 feed함수의 파라미터로 전달한다.
  3. Cat의 eat()를 호출한다.

위와 같은 방식으로 Swift는 Cat의 eat()을 직접 호출하는 static dispatch 방식을 사용하게된다.

// 1. existential type을 사용한 코드
func feed(to pet: Pet) {
    pet.eat()
}

// 2. 제네릭을 사용한 코드
func feed<T: Pet>(to pet: T) {
    pet.eat()
}

1번 코드가 2번에 비해 비용이 더많이 듦에도 불구하고 우리는 1번의 방식을 자주 사용했는데 이유는 다음과 같다.

  1. 코드를 더 적게 작성할수있다.
  2. 코드만 봤을때 1,2번을 비교해서 보면 existential type 을 사용했을때 비용이 더많을것이라는것이 드러나지 않기 때문이다.

existential type을 사용하는 비용은 숨겨져서는 안되며 개발자들은 이러한 의미 체계를 명시적으로 선택 해야한다 라는 뜻에서 existential any 라는것이 만들어지게 되었다.

Existential any

Swift5.6 에서 existential type과 any를 같이 쓸 수 있게 되었는데.
Swift6에서는 이를 명시하지 않으면 에러를 발생시키게할 예정이다.

아래의 예시 코드는 위에서 설명한 Generic을 사용하지 않은 existential type 을 활용한 코드다.

func feed(to pet: any Pet) {
    pet.eat()
}

함수를 정의할때 파라미터로 전달될 타입이 existential type 이라면 앞에 any 키워드를 작성해야 한다.

any키워드를 작성함으로써 어떤 타입인지 구체적으로 모른다는 뜻을 내포하는것 같다.

인스턴스를 생성 할때도 마찬가지다.
위의 예시코드에서는 다음과같이 생성해주었다.

var cat: Pet = Cat()

하지만 Swift6이후 부터는 아래의 예시코드처럼 작성하지 않으면 에러가 발생한다.

var cat: any Pet = Cat()

참고 사항

프로토콜이 아닌 타입에 대해 any를 붙이면 에러가난다!

var cat: any Cat = Cat() //에러 발생!

func generic<T>(t: T) {
    let x: any T = t // 에러 발생!
}

let y: any (Int) -> Void = generic // 에러 발생!

프로토콜의 Metatype 에도 any를 붙여줘야 한다!

protocol P {}
struct S: P {}

let existentialMetatype: any P.Type = S.self

P.Type 으로 사용하던 녀석은 any P.Type
P.self -> (any P).self

참고한 자료 및 문서

https://zeddios.tistory.com/1348
https://docs.swift.org/swift-book/LanguageGuide/Protocols.html
https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md
https://www.hackingwithswift.com/swift/5.6/existential-any

'iOS > Swift-Memo' 카테고리의 다른 글

defer 란 무엇인가?  (0) 2022.07.05
ARC 자동 참조 카운팅 (Automatic Reference Counting)  (0) 2022.07.04
Hash Table과 Hashable  (0) 2022.06.18
UML(Unified Modeling Language)  (0) 2022.04.29
ARC, 순환참조 해결법  (0) 2022.04.29
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함