스터디 공유주제 두 번째: 타입 캐스팅 (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)
}
}
위와 같이 MediaItem
클래스를 상속받는 Movie
클래스, Song
클래스가 있다.
let titanicUpcast = titanic as Mediaitem
과 같이 Upcast 한다. Upcast는 항상 성공한다. 당연함.
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")
]
이건 library 배열인데, 타입 추론시 [MediaItem]
으로 뜰 것이다.
Movie, Song이 MediaItem으로 업캐스트 되었다.
위 배열에 대해 다운캐스팅 성공시 출력하는 for 문을 만든다면,
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)")
}
}
이렇게 뜬다.
같은 클래스로 다운캐스트하면 어떻게 되는지 궁금해서 해봤는데, 아래와 같은 경고창이 뜬다.
if let titanicDowncast = titanic as? Movie {
print("Downcast Succeeded")
}
같은 클래스로 캐스팅하면 언제나 성공한다는 경고창.
업캐스팅 되지 않은 상위 클래스에서 다운캐스트 했을 때 어떻게 되는지 궁금했음.
정리하고 보니까 당연한 말인데, 글로 보기만 하면 또 와닿지가 않아...
위의 예제에서 중간에 MediaItem()을 추가해 본다.
let library = \[
Movie(name: "Casablanca", director: "Michael Curtiz"),
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
MediaItem(name: "Seung Hui"),
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")
]
똑같이 for문을 돌려서 출력해보면...
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)")
}
}
MediaItem(name: "Seung Hui") 에 대해서는 다운캐스팅이 실패한다.
왜냐면,
다운캐스팅을 할 때 어떤 인스턴스의 현재 클래스가 아니라, 그 인스턴스의 실제 타입이 타겟 타입과 맞는지 확인한다.
library에 묶여서 Movie, Song 객체들이 MediaItem으로 업캐스팅 되었다 하더라도,
그 각각의 인스턴스의 실제 타입은 Media, Song 이다.
이를 타겟하여 다운캐스팅을 한다면, 타겟 타입과 인스턴스의 타입이 맞는지 비교하고 캐스팅에 성공하던 실패하던 하는 것 (같다)
Because item is a MediaItem instance, it’s possible that it might be a Movie; equally, it’s also possible that it might be a Song, or even just a base MediaItem. Because of this uncertainty, the as? form of the type cast operator returns an optional value when attempting to downcast to a subclass type. The result of item as? Movie is of type Movie?, or “optional Movie”.
Downcasting to Movie fails when applied to the Song instances in the library array. To cope with this, the example above uses optional binding to check whether the optional Movie actually contains a value (that is, to find out whether the downcast succeeded.) This optional binding is written “if let movie = item as? Movie”, which can be read as:
“Try to access item as a Movie. If this is successful, set a new temporary constant called movie to the value stored in the returned optional Movie.”
하지만 MediaItem(name: "Seung Hui")은 Movie, Song도 아닌 그냥 MediaItem 타입인 부모 클래스의 인스턴스일 뿐이다.
[참고] 소들이님 블로그에서 긁어온 업캐스팅/다운캐스팅 시 메모리 구조
업캐스팅 | 다운캐스팅 |
---|---|
![]() | ![]() |
Swift 내부 메서드나 API 사용 시 범용적인 리턴(또는 Any)을 주는 경우가 많은 것 같음.
guard let data = data,
let response = response as? HTTPURLResponse,
(200..<300).contains(response.statusCode) else {
URLResponse -> HTTPURLResponse 다운캐스팅
guard let cell = collectionView
.dequeueReusableCell(withReuseIdentifier: TingCollectionViewCell.id,
for: indexPath) as? TingCollectionViewCell else {
return UICollectionViewCell()
}
CollectionViewCell -> CustomCollectionViewCell 다운캐스팅
말고 또 언제 쓸까요? 개발끈이 짧아서 모르겠다...
+ bridge 기능
+
스터디 내용 추가:
NS 어쩌구는 Objective - C 애들 (foundation 이 obj-c 애들)
uikit이랑의 관계성
Any 사용 위험하니 제네릭 사용 하면 좋다는 맥락이 맞는지
input output
clean architecture 할 때 오타나면 뜹니다