iOS Tip: 도대체 some이 뭐야?

Lee young-jun
6 min readNov 3, 2023

--

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

--

--

No responses yet