C에서는 타입캐스팅을 굉장히 많이 해 봤는데 SwiftUI를 다루면서 타입캐스팅을 별로 안 해 본 것 같다. 한번 배워보자구~
Type Casting은 인스턴스의 타입을 체크하거나, 이 인스턴스의 해당 클래스를 다른 슈퍼클래스나 서브클래스로 다루는 것을 의미함.
스위프트에서 타입캐스팅은 is
와 as
연산자로 구현돼 있다. 이 두 연산자가 타입을 체크하고 다른 타입으로 바꾸는 역할을 해 줌.
타입캐스팅을 클래스 간의 계층이 있을 때도 사용할 수 있음!
클래스 인스턴스를 같은 계층의 다른 클래스 인스턴스 타입으로 변환할 수가 있다.
무슨 말인지 모르겠지만... 예제를 보도록 하자.
베이스 클래스인 MediaItem
이다.
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
고다음 두개는 MediaItem
을 상속받는 서브클래스임.
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)
}
}
각각 프로퍼티를 추가하고 그에 맞는 이니셜라이저도 추가했다.
얘네들을 사용해 보도록 하자.
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]
library
배열은 두 서브클래스들의 인스턴스를 원소로 갖는다. 그럼 library
의 타입은?!
Swift의 type checker가 추정하기로는 Movie
와 Song
이 MediaItem
이라는 동일한 슈퍼클래스를 갖고 있으므로 library
배열을 MediaItem
의 타입으로 인식해버린다.
배열 안에 있는 원소들은 Movie와 Song 인스턴스이긴 하지만, 이 배열을 쭉 iterate 해보면 MediaItem 타입의 값을 반환받는다.
원래 타입으로 작업하고 싶다면
is
가 바로 type check 연산자임!
얘는 인스턴스가 특정 서브클래스 타입인지 체크하고 맞다면 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"
library
배열에 Movie 와 Song 인스턴스가 몇 개인지 세는 코드임.
item is Movie
에서 지금 item은 MediaItem
타입인데, 이게 얘의 서브클래스인 Movie
인스턴스인지 판단하는 거임. Song
도 마찬가지.
만약 클래스 인스턴스가 서브클래스의 인스턴스와 동일하다면, as
를 이용해서 서브클래스 타입으로 만들어줄 수 있음! as?
나 as!
로 사용함.
다운캐스팅이 실패할 수도 있어서 두 가지 모양으로 사용하는데
as?
는 다운캐스팅 하려는 타입의 옵셔널 값을 반환해줌.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
여기서 배열의 각 아이템들은 Movie
이거나 Song
이여야 하는데, 아이템마다 어떤 클래스를 써야 하는지 확실하게 알지 못하기 때문에 as?
를 써서 다운캐스팅 해야 함!
item as? Movie
는 Movie?
타입의 결과를 가지기 때문에 if let 구문으로 언래핑해서 movie에 넣어준 것!
참고
캐스팅은 인스턴스를 바꾸거나 값을 바꾸지 않는다. 기존 인스턴스는 동일하게 유지되고, 캐스팅된 인스턴스 타입으로 취급만 되는 것!
Swift는 두 가지 특별한 타입을 갖구있다.
Any
는 함수 타입을 포함해 모든 타입 인스턴스를 표현할 수 있음.AnyObject
는 모든 클래스 타입의 인스턴스를 표현할 수 있음.얘네가 제공하는 역할과 기능이 필요할 때만 써야 한다. 코드 쓸때는 타입을 명확하게 쓰는 게 좋기 때무네.
Any를 쓰는 경우를 예제로 살펴보자.
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)" })
thing
배열은 Any
타입인데, 말그대로 함수를 포함한 모든 타입을 넣을 수 있음.
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
이런식으로 iterate할 수 있다.