Swift 언어에서 타입 캐스팅 메커니즘은 실행 시 값이 한 타입에서 다른 타입으로 변환되는지 확인하고 변환하는 문제를 해결합니다. 이는 정적 타입 지정과 상속에서 비롯됩니다: Swift에서는 value 타입 (struct/enum)과 reference 타입 (class/protocol) 모두와 동시에 작업할 수 있습니다. 문제가 발생하는 것은 객체나 값이 Any 타입 또는 프로토콜 타입 변수에 저장될 때이며, 이를 다른 (더 구체적인) 타입으로 변환할 수 있는지 검사해야 할 때입니다. 이때 애플리케이션의 충돌 위험 없이 변환할 수 있는지를 확인해야 합니다.
안전한 타입 캐스팅을 통해 타입 안전성, 동적 다형성의 이점을 활용하고 혼합된 컬렉션 또는 클래스 계층에서 작업할 때 오류를 방지합니다.
Swift는 세 가지 형태의 타입 캐스팅을 제공합니다:
as — 안전한 캐스팅 (upcast), 컴파일러가 항상 결과를 미리 알 수 있습니다.as? — 조건부 (optional) 캐스팅 (downcast), 변환할 수 없을 경우 optional을 반환합니다.as! — 강제 (forced) 캐스팅, 변환할 수 없다면 런타임 충돌을 발생시킵니다.코드 예제:
class Animal {} class Dog: Animal { func bark() { print("Woof!") } } let animals: [Animal] = [Dog(), Animal()] for animal in animals { if let dog = animal as? Dog { dog.bark() // 안전한 캐스팅 as? } else { print("Not a dog!") } }
주요 특징:
상관없는 타입 간(예: String에서 Int로)으로 as! 연산자를 사용할 수 있나요? 그 결과는 무엇입니까?
as! 연산자는 항상 런타임 충돌을 초래합니다. 타입이 호환되지 않거나 두 타입 간 상속이나 공통 프로토콜 계층이 없는 경우입니다. 이는 근본적으로 다른 타입 간 변환을 허용하지 않습니다.
코드 예제:
let value: Any = "abc" let num = value as! Int // 충돌: 'String'을 'Int'로 캐스팅할 수 없음
상속 계층이 전혀 없는 타입으로 as?를 사용할 경우 어떻게 됩니까?
as?는 기본적으로 타입을 안전하게 변환할 수 없는 경우 항상 nil을 반환합니다. 이는 타입 간 상속이나 프로토콜이 전혀 연결되어 있지 않은 경우에도 마찬가지입니다.
코드 예제:
let value: Any = 5 if let str = value as? String { print(str) } else { print("Can't cast to String") // 이 블록이 실행됩니다. }
downcasting을 위해 as를 사용할 수 있나요?
아니요. as 연산자는 upcast 전용입니다 (예: Dog에서 Animal로, 구현된 프로토콜로 변환할 때). 다운캐스팅에는 항상 as? 또는 as!가 사용됩니다.
코드 예제:
let animal: Animal = Dog() // let dog = animal as Dog // 컴파일 오류 발생 let dog = animal as? Dog // 올바른 방법
프로젝트에서 [Any] 타입의 컬렉션이 있었고 실수로 문자열과 숫자가 포함되었습니다. 데이터 처리를 위해 개발자는 여러 곳에서 let value = item as! String을 작성하여 배열에 숫자가 있을 경우 충돌을 일으켰습니다.
장점:
단점:
연관 타입 enum을 사용하여 다시 작성했습니다:
enum Payload { case text(String); case number(Int) } let data: [Payload] = [.text("abc"), .number(1)]
장점:
단점: