배경
- 아무 생각 없이 프로토콜을 작성하다가... associatedtype을 사용한 프로토콜을 타입으로 사용할 땐 any를 붙여줘야 한다는 warning을 보게 되었다.
protocol SomeProtocol {
associatedtype SomeType
}
let someProperty: any SomeProtocol
- associatedtype을 사용하지 않은 프로토콜은 any를 붙여주지 않아도 아래와 같이 타입으로 선언 가능했다.
protocol SomeProtocol {
}
let someProperty: SomeProtocol
any는 어디서 나온 것인고?
- 관련해서 찾아보던 중, 이는 Swift 5.6에서 제안된 키워드임을 알 수 있었다.
- Swift Evolution 중 SE-0335 Proposal을 살펴보면 existential any 를 제안하며 프로토콜과 같은 existential 타입 앞에 any를 붙여야 하는 이유를 설명해주고 있다.
Existential 타입이 뭔데?
- 프로토콜을 사용한 배열을 생각해보자.
- 해당 배열에는 프로토콜을 준수하는 어떤 타입이던 들어갈 수 있다.
- 그런데 배열은 각각의 요소가 차지하는 메모리 공간이 모두 똑같은데, 어떻게 서로 다른 크기의 타입 인스턴스들이 같은 배열에 들어갈 수 있는 것일까?
- Swift는 Existential Container라는 특별한 저장 레이아웃을 사용해 이 문제를 해결한다.
- 사실 배열에는 각각의 인스턴스가 아닌, Existential Container가 저장되고, 해당 Existential Container가 각각의 인스턴스 정보를 관리하고 있었던 것이다.
- 프로토콜을 타입으로 사용할 경우, 이렇든 Existential Container에 의존하기 때문에 Existential type이라고 한다.
- 따라서 Existential type은 프로토콜 타입을 뜻함!
any를 붙여야 하는 이유
- Swift의 Existential 타입은 굉장히 가벼운 문법을 가지고 있다.
- 그러나 그에 비해 Existential 타입이 가지는 임팩트는 크다.
- 프로토콜 타입은 구체 타입 인스턴스가 아닌, Existential Container를 통해 각 인스턴스를 관리하게 된다.
- 따라서 함수 호출이 동적으로 동작(dynamic dispatch)하게 되며, 이는 무겁게 동작한다.
- 값 타입을 저장하더라도 3 word 크기를 넘어가면 Existential Container가 관리하는 별도의 공간에 저장된 후 Existential Container에 의해 관리된다.
- 따라서 힙 할당 오버헤드도 발생~~!!
- 그래서 꼭 필요한 경우가 아니라면 쓰지 않는 것이 좋음
- 따라서 any를 붙여 이것이 Existential 타입임을 명시적으로 나타내어 혼란을 줄이자는 것이 주요 내용이다.
- 아무 생각 없이 프로토콜 타입 쓰지 않게
- any로 구분짓자!
Swift 6에서의 Existential any
- Swift 6부터는 Existential type 에서의 any가 필수적이도록 변경된다고 한다.
- 따라서 프로토콜 타입을 선언할 땐 any를 붙여줘야 함
- 사용자의 실수를 방지하는 안정성 + 가독성 좋은 언어라는 지향점에 한발 더 가까이 다가간 것 같아서 기분좋음~