Type casting(타입 캐스팅) 이란 인스턴스의 타입을 체크하거나, 인스턴스를 다른 상위 클래스 또는 하위클래스의 인스턴스로 취급하는 것을 의미합니다.
Swift 의 타입캐스팅은 is 와 as 연산자를 사용합니다. 이 두 연산자를 사용하면 간단하고 효과적인 방식으로 값의 타입을 체크하거나 값을 다른 타입으로 변환할 수 있습니다.
타입 캐스팅을 프로토콜의 준수 여부를 확인하는데 사용할 수도 있습니다.
Defining a Class Hierarchy for Type Casting
타입 캐스팅을 사용하면 클래스의 계층 구조 내에서 특정 클래스의 인스턴스의 타입을 확인할 수 있고, 같은 계층구조 내의 다른 클래스의 인스턴스로 캐스팅할 수 있습니다.
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
Swift
복사
아래 코드에서는, Movie 인스턴스 두개 Song 인스턴스 3개를 포함하는 library 어레이를 생성했습니다. library 어레이의 타입은 어레이 리터럴의 컨텐츠로부터 타입이 추론됩니다. Swift 의 타입 체커는 Movie 와 Song 가 동일한 상위 클래스인 MediaItem 를 갖는 것을 확인 후 library 의 타입을 [MediaItem] 으로 추론합니다.
let library = [
Movie(name: "Casablanca", director: "Michael Curtiz"),
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
Movie(name: "Citizen Kane", director: "Orson Welles"),
Song(name: "The One And Only", artist: "Chesney Hawkes"),
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
// the type of "library" is inferred to be [MediaItem]
Swift
복사
library 에는 Movie 와 Song 타입의 인스턴스가 저장되어 있습니다.
하지만 이 어레이의 항목들을 순회한다면, 항목들은 MediaItem 타입으로 반환됩니다.
온전한 타입으로 다루기 위해서는 각각의 타입을 체크하거나, 다른 타입으로 downcast 해야합니다.
이는 아래 섹션에서 다루겠습니다.
Checking Type
타입 체크 연산자 is 를 사용하면 특정 하위클래스 타입의 인스턴스인지를 확인할 수 있습니다.
타입 체크 연산자는 해당 인스턴스의 타입이 특정 하위클래스의 타입과 일치하면 true 불일치하면 false를 리턴합니다.
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}
}
print("Media library contains \(movieCount) movies and \(songCount) songs")
// Prints "Media library contains 2 movies and 3 songs"
Swift
복사
Downcasting
특정 클래스 타입의 상수 또는 변수가 하위클래스의 인스턴스일것이라고 생각된다면, as? 또는 as! 를 사용해서 class 를 다운캐스팅하세요.
다운캐스팅은 실패할 수 있기 때문에 타입 캐스트 연산자는 두가지 형태를 갖습니다.
•
조건문의 형태인 as? 다운캐스팅하고자하는 타입의 optional 값을 리턴합니다.
•
forced 형태인 as! 는 forced unwrap 의 결과를 리턴합니다.
다운 캐스팅이 성공할지 확신이 안든다면 as? 를 사용하세요
다운캐스팅이 불가능한 경우 nil 값을 리턴합니다.
다운캐스팅이 확실하게 성공할 것이라 생각되면 as! 를 사용하세요
해당 구문은 다운캐스팅에 실패할 시 런타임에러를 발생시킵니다.
for item in library {
if let movie = item as? Movie {
print("Movie: \(movie.name), dir. \(movie.director)")
} else if let song = item as? Song {
print("Song: \(song.name), by \(song.artist)")
}
}
// Movie: Casablanca, dir. Michael Curtiz
// Song: Blue Suede Shoes, by Elvis Presley
// Movie: Citizen Kane, dir. Orson Welles
// Song: The One And Only, by Chesney Hawkes
// Song: Never Gonna Give You Up, by Rick Astley
Swift
복사
item 이 Movie 타입일지 확신할 수 없기 때문에 as? 를 사용한 것을 볼 수 있습니다
as? 연산자를 사용한 다운캐스팅은 optional 타입을 리턴합니다.
따라서 반환된 값을 사용하고, nil 값에 대응하기 위해서 optional binding 을 사용한 모습입니다.
캐스팅은 인스턴스의 원본을 변경하거나 값을 바꾸지 않습니다. 원본 인스턴스는 그대로 유지됩니다. 단순히 캐스팅된 타입의 인스턴스로서 접근이 가능하고 처리가 가능할 뿐입니다.
Type Casting for Any and AnyObject
Swift 에서는 구체적이지 않은 타입을 다루기 위해 두가지의 특별한 타입을 제공합니다.
•
Any : 함수 타입을 포함한 모든 타입의 인스턴스를 나타냅니다.(클래스 타입 미포함)
•
AnyObject : 모든 클래스 타입의 인스턴스를 나타냅니다.
이러한 편리한 기능을 제공하지만 Any, AnyObject를 사용하는 것보다는 실제 작업에 사용될 타입을 구체적으로 지정하는 것이 가장 좋다.
var things: [Any] = []
things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })
Swift
복사
다양한 타입의 값들이 things 어레이에 들어있는 모습이다.
Any, AnyObject 로 나타내어진 변수들의 타입을 확인하기 위해서는 is 또는 as 패턴을 switch 문과 함께 사용하면 됩니다.
아래의 항목들은 switch 문 내부에서 각각의 항목의 타입을 검사하며 순회합니다.
for thing in things {
switch thing {
case 0 as Int:
print("zero as an Int")
case 0 as Double:
print("zero as a Double")
case let someInt as Int:
print("an integer value of \(someInt)")
case let someDouble as Double where someDouble > 0:
print("a positive double value of \(someDouble)")
case is Double:
print("some other double value that I don't want to print")
case let someString as String:
print("a string value of \"\(someString)\"")
case let (x, y) as (Double, Double):
print("an (x, y) point at \(x), \(y)")
case let movie as Movie:
print("a movie called \(movie.name), dir. \(movie.director)")
case let stringConverter as (String) -> String:
print(stringConverter("Michael"))
default:
print("something else")
}
}
// zero as an Int
// zero as a Double
// an integer value of 42
// a positive double value of 3.14159
// a string value of "hello"
// an (x, y) point at 3.0, 5.0
// a movie called Ghostbusters, dir. Ivan Reitman
// Hello, Michael
Swift
복사
타입 검사와 동시에
let someInt as Int 와 같이 새로운 상수에 값을 저장과 동시에 다운캐스팅하여 case 문 내부에서 사용이 가능합니다.
where 키워드를 사용해 조건을 더할 수도 있습니다.
Any 타입은 optional 타입을 포함한 모든 타입을 나타냅니다. Any 타입의 값이 사용될 것으로 예상되는 곳에 optional 값을 사용하면 Swift 는 경고메세지를 띄웁니다. 만약 optional 값을 사용하고자 한다면 as 연산자를 사용해서 Any 타입으로 타입 캐스팅을 해주면 경고메세지를 지울 수 있습니다.
let optionalNumber: Int? = 3
things.append(optionalNumber) // Warning
things.append(optionalNumber as Any) // No warning
Swift
복사