iOS Tip: 도대체 some이 뭐야?
SwiftUI를 시작했다면 아마 이런 코드를 봤을거에요. 오늘은 이게 뭔지 알아보기로 했어요.
var body: some View {
//...
}
Protocol
여러분이 스타크래프트를 만든다고 하면 아래와 같이 유닛을 정의하고
protocol Unit {
func move()
}
struct Marine: Unit {
func move() {
/***/
}
}
struct Firebat: Unit {
func move() {
/***/
}
}
struct Medic: Unit {
func move() {
/***/
}
}
사용자는 사령관이 되어서 유닛을 움직일 거에요. 😀
struct Commander {
func move(_ unit: Unit) -> Unit { unit }
}
var commander = Commander.init()
var unit = Marine.init()
var movedUnit = commander.move(unit)
여기까지는 잘 돌아가는데 만약 무기를 추가하면 어떻게 될까요? 🤔
associatedtype
이렇게 무기를 정의하고 Unit에 무기 Type을 지정할 수 있게 한 후
protocol Weapon {
func attack()
}
struct Gun: Weapon {
func attack() {
//
}
}
struct Fire: Weapon {
func attack() {
//
}
}
struct Heal: Weapon {
func attack() {
//
}
}
protocol Unit {
associatedtype WeaponType where WeaponType : Weapon
func move()
}
유닛별로 다른 무기를 줍니다.
struct Marine: Unit {
typealias WeaponType = Gun
func move() {
/***/
}
}
struct Firebat: Unit {
typealias WeaponType = Fire
func move() {
/***/
}
}
struct Medic: Unit {
typealias WeaponType = Heal
func move() {
/***/
}
}
그러면 못보던 오류가 나타나요. 😭
error: playground.playground:8:23: error: use of protocol 'Unit' as a type must be written 'any Unit'
func move(_ unit: Unit) -> Unit { unit }
^~~~
any Unit
아니 이게 뭐죠?? any Unit으로 바꾸라는데…
일단 Unit에 추가된 type 특정할 수 없어서 그런것 같으니 Generic으로 바꿔봤더니 다시 잘 됩니다. 😃
struct Commander {
func move<U>(_ unit: U) -> U where U: Unit { unit }
}
그럼 Some으로 바꾸면 어떻게 되는지 볼까요? (오류는 any로 바꾸라고했지만.. any는 다음 기회에 알아봐요..)
struct Commander {
func move(_ unit: some Unit) -> some Unit { unit }
}
이것도 성공이에요! 🎉 그리고 코드도 더 깔끔해졌죠!
Limitation
그러나 한계도 있는데요. Generic을 사용한 경우는 반환 Type이 명확한 반면
some을 사용한 경우는 some으로 밖에 알 수가 없고,
원래 Type으로 Casting하려고 하면
var movedUnit: Marine = commander.move(unit)
아래와 같은 오류가 발생합니다.
error: playground.playground:84:35: error: cannot convert value of type 'some Unit' to specified type 'Marine'
var movedUnit: Marine = commander.move(unit)
~~~~~~~~~~^~~~~~~~~~
as! Marine
물론 오류에 설명된 것 처럼 강제 형변환하면 되지만요. 😄
그리고 아래와 같이 연관 Type에 제약을 두는 경우. 아래와 같이 Generic으로는 되지만
func move<U>(_ unit: U) -> U where U: Unit, U.WeaponType == Gun { unit }
some으로 아래와 같이 만들면
func move(_ unit: some Unit) -> some Unit where Unit.WeaponType == Gun { unit }
이런 오류가 나네요 😭
error: playground.playground:12:58: error: cannot access associated type 'WeaponType' from 'Unit'; use a concrete type or generic parameter base instead
func move(_ unit: some Unit) -> some Unit where Unit.WeaponType == Gun { unit }
정리하면 연관된 Type을 가진 Protocol로 묶어주는 경우에는 Generic 아니면 some을 사용해야 한다 가 될 것 같네요.
원문
https://medium.com/@paulwall_21/the-some-keyword-in-swift-5e38271d646e