타입캐스팅 (Type Casting)
타입캐스팅은 인스턴스의 타입을 확인하거나 인스턴스를 같은 계층(hierachy)에 있는 다른 superclasssubclass로 취급하는 방법입니다. 타입캐스팅에는 isas 두 연산자를 사용합니다. 타입캐스팅을 이용하면 특정 프로토콜을 따르는지(conforms) 확인할 수도 있습니다.

타입캐스팅을 위한 클래스 계층구조 선언 (Defining a Class Hierarchy for Type Casting)

타입캐스팅의 동작을 확인하기 위해 클래스를 하나 만들어 보겠습니다.
1
class MediaItem {
2
var name: String
3
init(name: String) {
4
self.name = name
5
}
6
}
Copied!
MediaItem이라는 클래스를 선언했는데 이 클래스를 서브클래싱 해서 두개의 다른 서브클래스를 만들어 보겠습니다.
1
class Movie: MediaItem {
2
var director: String
3
init(name: String, director: String) {
4
self.director = director
5
super.init(name: name)
6
}
7
}
8
9
class Song: MediaItem {
10
var artist: String
11
init(name: String, artist: String) {
12
self.artist = artist
13
super.init(name: name)
14
}
15
}
Copied!
MediaItem클래스의 서브클래스 MovieSong 두개의 클래스를 선언했습니다. 마지막으로 이 MovieSong 두개의 클래스를 아이템으로 갖는 library배열을 선언합니다.
1
let library = [
2
Movie(name: "Casablanca", director: "Michael Curtiz"),
3
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
4
Movie(name: "Citizen Kane", director: "Orson Welles"),
5
Song(name: "The One And Only", artist: "Chesney Hawkes"),
6
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
7
]
8
// the type of "library" is inferred to be [MediaItem]
Copied!
library가 갖고 있는 Movie,Song인스턴스의 공통 부모는 MediaItem이기 때문에 library는 타입 추론에 의해 [MediaItem] 배열의 형을 갖게 됩니다. library를 순회(iterate)하면 배열의 아이템은 Movie, Song 타입이 아니라 MediaItem타입이라는 것을 확인할 수 있습니다. 타입 지정을 위해서는 downcasting을 이용해야 합니다.

형 확인 (Checking Type)

is연산자를 이용해 특정 인스턴스의 타입을 확인할 수 있습니다. 아래 코드는 library배열을 순회하고 아이템이 특정 타입일때마다 그 숫자를 증가하는 예제 코드입니다.
1
var movieCount = 0
2
var songCount = 0
3
4
for item in library {
5
if item is Movie {
6
movieCount += 1
7
} else if item is Song {
8
songCount += 1
9
}
10
}
11
12
print("Media library contains \(movieCount) movies and \(songCount) songs")
13
// "Media library contains 2 movies and 3 songs" 출력
Copied!

다운캐스팅 (Downcasting)

특정 클래스 타입의 상수나 변수는 특정 서브클래스의 인스턴스를 참조하고 있을 수 있습니다. as?as!연산자를 이용해 어떤 타입의 인스턴스인지 확인할 수 있습니다. as?는 특정 타입이 맞는지 확신할 수 없을때 사용하고 as!는 특정 타입이라는 것이 확실한 경우에 사용합니다. 단 as!으로 다운캐스팅을 했는데 지정한 타입이 아니라면 런타임 에러가 발생합니다. 다음 예제는 library배열의 mediaItemMovie 인스턴스 일수도 있고 Song인스턴스일 수도 있기 때문에 다운캐스팅을 위해 as?연산자를 사용했습니다.
1
for item in library {
2
if let movie = item as? Movie {
3
print("Movie: \(movie.name), dir. \(movie.director)")
4
} else if let song = item as? Song {
5
print("Song: \(song.name), by \(song.artist)")
6
}
7
}
8
9
// Movie: Casablanca, dir. Michael Curtiz
10
// Song: Blue Suede Shoes, by Elvis Presley
11
// Movie: Citizen Kane, dir. Orson Welles
12
// Song: The One And Only, by Chesney Hawkes
13
// Song: Never Gonna Give You Up, by Rick Astley
Copied!
NOTE 캐스팅은 실제 인스턴스나 값을 바꾸는 것이 아니라 지정한 타입으로 취급하는 것 뿐입니다.

Any, AnyObject의 타입 캐스팅 (Type Casting for Any and AnyObject)

Swift에서는 두가지 특별한 타입을 제공합니다.
  • Any : 함수 타입을 포함해 모든 타입을 나타냅니다.
  • AnyObject : 모든 클래스 타임의 인스턴스를 나타냅니다.
    Any타입의 예제입니다. things라는 Any타입 배열을 선언해 여러 타입의 값을 저장합니다. 여기에는 Int, String, 함수, 클로저까지 포함됩니다.
1
var things = [Any]()
2
3
things.append(0)
4
things.append(0.0)
5
things.append(42)
6
things.append(3.14159)
7
things.append("hello")
8
things.append((3.0, 5.0))
9
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
10
things.append({ (name: String) -> String in "Hello, \(name)" })
Copied!
things를 순회하며 타입캐스팅이 되는지 switch case문에 as 연산자로 확인해 타입캐스팅 되는 배열의 원소의 값을 적절히 출력합니다.
1
for thing in things {
2
switch thing {
3
case 0 as Int:
4
print("zero as an Int")
5
case 0 as Double:
6
print("zero as a Double")
7
case let someInt as Int:
8
print("an integer value of \(someInt)")
9
case let someDouble as Double where someDouble > 0:
10
print("a positive double value of \(someDouble)")
11
case is Double:
12
print("some other double value that I don't want to print")
13
case let someString as String:
14
print("a string value of \"\(someString)\"")
15
case let (x, y) as (Double, Double):
16
print("an (x, y) point at \(x), \(y)")
17
case let movie as Movie:
18
print("a movie called \(movie.name), dir. \(movie.director)")
19
case let stringConverter as (String) -> String:
20
print(stringConverter("Michael"))
21
default:
22
print("something else")
23
}
24
}
Copied!
Int, Double뿐만 아니라 튜플, 함수도 Any타입에 포함될 수 있다는 것을 확인할 수 있습니다.
NOTE Any 타입은 옵셔널 타입을 포함합니다. 하지만 Swift에서는 Any타입을 사용해야 하는 곳에 옵셔널을 사용하면 경고를 발생 시킵니다. let optionalNumber: Int? = 3 things.append(optionalNumber) // 경고 things.append(optionalNumber as Any) // 경고 없음