Swift Type Casting
Swift Type casting is a way to check the type of an instance, or to treat that instance as a different superclass or subclass from somewhere else in its own class hierarchy.
1. Type Casting π©βπ»
1. What is Type Casting?
Type Casting
μ λ¨μν νμ
μ λ€λ₯Έ νμ
μΌλ‘ λ³κ²½νλ κ²λ§μ μλ―Ένλ κ²μ΄ μλλ€. Instance μ νμ
μ νμΈ
νκ±°λ,
ν΄λΉ Instance λ₯Ό μμ μ Class Hierarchy
ꡬ쑰 μμμ Superclass
λλ Subclass
λ‘ λ€λ£¨κΈ° μν΄ μ¬μ©νλ€.
Swift μμ Type Casting μ is
μ as
operators λ₯Ό μ΄μ©ν΄ ꡬνλλ€. μ΄ λ operators λ κ°λ¨νλ©΄μ λ¬Έμ₯μ νννλ κ²κ³Ό
κ°μ μμ°μ€λ¬μ΄ λ°©λ²μΌλ‘ Value
μ λν Checking Type
κ³Ό Casting Another Types
μ μ§μνλ€.
κ·Έλ¦¬κ³ Checking Typeμ ν΄λΉ Instance μ νμ
μ νμΈ
νλ κ² λΏ μλλΌ κ·Έ νμ
μ΄ νΉμ Protocols λ₯Ό λ°λ₯΄κ³ μλμ§ νμΈ
νλ
λ° μ¬μ©λκΈ°λ νλ€.
2. Defining a Class Hierarchy for Type Casting
- Base Class
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
- Subclass of the MediaItem
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
Movie μ instance λ§ μ μ₯ν κ²½μ° λ°°μ΄ movies λ [Movie]
νμ
μ λ°°μ΄λ‘ μΆλ‘ λλ€.
let movies = [
Movie(name: "Casablanca", director: "Michael Curtiz"),
Movie(name: "Citizen Kane", director: "Orson Welles")
]
print(type(of: movies)) // Array<Movie>
κ·Έλ¦¬κ³ movies λ λ€μκ³Ό κ°μ΄ λ°λ³΅μ΄ κ°λ₯νλ€.
movies.forEach { print("The director of \($0.name) is \($0.director).") }
The director of Casablanca is Michael Curtiz.
The director of Citizen Kane is Orson Welles.
μ΄μ MediaItem μ Superclass λ‘ κ°λ λ λ€λ₯Έ Subclass Song μ μΆκ°ν΄λ³΄μ.
- Subclass of the MediaItem
class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
λ€μ λ°°μ΄ library λ 2κ°μ Movie instances μ 3κ°μ Song instances λ₯Ό κ°μ§κ³ μλ€.
Swift μ Type Checker λ λ°°μ΄μ΄ μ΄κΈ°ν λ λ library μ elements κ° Movie μ Song νμ
μ΄λΌλ κ²μ νμΈ ν,
μ΄λ€μ΄ 곡ν΅λ Superclass MediaItem μ Class Hierarchy
ꡬ쑰λΌλ κ²μ μΆλ‘ νλ€. κ·Έλ¦¬κ³ λ νμ
μ λͺ¨λ μ μ₯νκΈ° μν΄
μ΄ λ°°μ΄μ [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")
]
print(type(of: library)) // Array<MediaItem>
κ·Έλ¦¬κ³ μ΄ λ°°μ΄μ λ°λ³΅μ ν΅ν΄ μ κ·Όνλ €κ³ νμΌλ MediaItem μ΄ κ°κ³ μλ name μ μ μΈν Subclasses Movie, Song μ
director μ artist μλ μ κ·Όμ΄ λΆκ°λ₯ν κ²μ νμΈν μ μλ€. Swift κ° λ νμ
μ λͺ¨λ μ μ₯νκΈ° μν΄ κ³΅ν΅λ Superclass νμ
μΌλ‘
λ°°μ΄μ μΆλ‘ νκΈ° λλ¬Έμ΄λ€. μ΄λ νμν κ²μ΄ λ°λ‘ Downcasting
μ΄λ€.
2. Checking Type (Type Check Operator βis
β) π©βπ»
Type Check Operator(is)
λ μΌμΉνλ νμ
μΈμ§ νμΈ ν Bool
μ λ°ννλ€.
let aMedia = MediaItem(name: "Avatar")
let aMovie = Movie(name: "Casablanca", director: "Michael Curtiz")
print(aMedia is MediaItem) // true
print(aMedia is Movie) // false
print(aMedia is Song) // false
print(aMovie is MediaItem) // true
print(aMovie is Movie) // true
print(aMovie is Song) // false
Superclass μ instance λ Subclass μ Members λ₯Ό λͺ¨λ κ°μ§ λͺ»νλ―λ‘ Subclass νμ
μ΄ λ μ μλ€.
λ°λ©΄ Subclass μ instance λ Superclass μ λͺ¨λ Members λ₯Ό λͺ¨λ κ°κ³ μμΌλ―λ‘, Superclass νμ
μ΄ λ μ μλ€.
μ library μ κ° νμ
μ΄ λͺ κ°μ© μ μ₯λμ΄ μλμ§ Type Check Operator
λ₯Ό μ¬μ©ν΄ νμΈν΄λ³΄μ.
var (movieCount, songCount) = (0, 0)
library.forEach {
switch $0 {
case is Movie: movieCount += 1
case is Song: songCount += 1
default: break
}
}
print("Media library contains \(movieCount) movies and \(songCount) songs")
Media library contains 2 movies and 3 songs
3. Downcasting (Type Cast Operator βas?
, as!
β) π©βπ»
νΉμ Class Type μ μμλ λ³μλ κ²μΌλ‘ 보μ΄λ κ²κ³Ό λ¬λ¦¬ μ€μ λ‘λ Subclass Instance λ₯Ό μ°Έμ‘°
νκ³ μλ κ²½μ°λ μλ€.
μμμ library κ° κ·Έλ° κ²½μ°λ€. μ΄λ μ΄κ²μ Type μ Subclass Type μΌλ‘ Downcasting
ν μ μλ€.
Downcasting μ μ€ν¨ν μ μκΈ° λλ¬Έμ 2κ°μ§μ Operators κ° μ 곡λλ€. μ‘°κ±΄λΆ νμ(conditional form)μΈ as?
λ
Optional
μ λ°ννλ―λ‘ Downcasting μ μ±κ³΅ μ¬λΆλ₯Ό νμΈ
νλ μ©λλ‘ μ¬μ©νλ€. λ§μ½ Downcasting μ±κ³΅ μ¬λΆλ₯Ό νμ ν μ μμ
κ²½μ°λ κ°μ νμ(forced form)μΈ as!
λ₯Ό μ¬μ©ν΄ Forced Unwrapping
λ νμ
μ μ»μ μ μλ€. λ¨, Downcasting μ΄ μ ν¨νμ§
μμ κ²½μ° Runtime Error κ° trigger λλ―λ‘ λ°λμ μ±κ³΅ν¨μ νμ ν μ μμ λλ§ μ¬μ©ν΄μΌνλ€.
Casting
μμ€μ instance λ₯Ό μμ νκ±°λ κ°μ λ°κΎΈμ§ μλλ€
. instance λ κ·Έλλ‘ μ μ§νλ©΄μ, λ¨μ§ casting λ νμ μ instance λ‘ λ€λ£¨κ³ μ κ·Όν μ μλλ‘ νλ€.
library.forEach {
if let movie = $0 as? Movie {
print("Movie: \(movie.name), dir. \(movie.director)")
} else if let song = $0 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
4. Type Casting for Any and AnyObject(Upcasting Operator βas
β) π©βπ»
Swift λ λΆνΉμ νμ μ μν λ κ°μ§μ νΉλ³ν νμ μ μ 곡νλ€.
- Any : Closure, Function, Class, Structure, Enumeration Types λ₯Ό ν¬ν¨ν λͺ¨λ νμ μ instance λ₯Ό λμ ν μ μλ€.
- AnyObject : Class Types λ₯Ό λμ ν μ μλ€.
Any
μAnyObject
λ μ΄κ²μ΄ μ 곡νλ μλ λ° κΈ°λ₯μ΄ λͺ μμ μΌλ‘ νμν κ²½μ°μλ§ μ¬μ©ν΄μΌνλ€. κ·Έ μΈ κ²½μ°λ μΈμ λ λͺ νν νμ μ μ§μ νλ κ²μ΄ λ μ’λ€.
Any
λOptional μ ν¬ν¨ν λͺ¨λ νμ μ λμ
ν μ μλ€.
μμ λ₯Ό μν΄ Structure μ Enumeration μ νλμ© μΆκ°νμ.
struct Point {
var x = 0.0, y = 0.0
}
enum CompassPoint {
case east, west, south, north
}
1. Any
var things: [Any] = []
func testAnyTypes(_ things: [Any]) {
for thing in things {
switch thing {
case 0 as Int:
print("\(thing) : zero as an Int")
case 0 as Double:
print("\(thing) : zero as a Double")
case let someInt as Int:
print("\(thing) : an integer value of \(someInt)")
case let someDouble as Double where someDouble > 0:
print("\(thing) : 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("\(thing) : a string value of \"\(someString)\"")
case let (x, y) as (Double, Double):
print("\(thing) : an (x, y) point at \(x), \(y)")
case let stringConverter as (String) -> String:
print("\(thing) : \(stringConverter("Michael"))")
case let movie as Movie:
print("\(thing) : a movie called \(movie.name), dir. \(movie.director)")
case let point as Point:
print("\(thing) : a point is at (\(point.x), \(point.y))")
case let direction as CompassPoint:
print("\(thing) : a direction is \(direction)")
default:
print("\(thing) : something else")
}
}
}
[Any]
μ μ¬λ¬ νμ
μ μ μ₯νκ³ , μ΄λ₯Ό Upcasting
μ ν΅ν΄ λ€μ νμΈν΄λ³΄μ.
- Int and Double
things.append(0) // Int
things.append(0.0) // Double
things.append(42) // Int
things.append(3.14159) // Double
testAnyTypes(things)
0 : zero as an Int
0.0 : zero as a Double
42 : an integer value of 42
3.14159 : a positive double value of 3.14159
- String, Tuple and Closure
things.append("hello") // String
things.append((3.0, 5.0)) // Tuple of type (Double, Double)
things.append({ (name: String) -> String in "Hello, \(name)" }) // Closure of type (name: Stirng) -> String
testAnyTypes(things)
hello : a string value of "hello"
(3.0, 5.0) : an (x, y) point at 3.0, 5.0
(Function) : Hello, Michael
- Class, Structure and Enumeration
things.append(Movie(name: "Avatar", director: "James Francis Cameron")) // Class
things.append(Point(x: 5.2, y: 3.0)) // Structure
things.append(CompassPoint.east) // Enumeration
testAnyTypes(things)
__lldb_expr_81.Movie : a movie called Avatar, dir. James Francis Cameron
Point(x: 5.2, y: 3.0) : a point is at (5.2, 3.0)
east : a direction is east
2. AnyObject
AnyObject
λ Any
μ λ¬λ¦¬ μ€μ§ Class Types
λ§ λμ ν μ μλ€.
var things: [AnyObject] = []
things.append(Movie(name: "Avatar", director: "James Francis Cameron")) // Class
if let aMovie = things[0] as? Movie {
print("\(aMovie) : a movie called \(aMovie.name), dir. \(aMovie.director)")
}
__lldb_expr_92.Movie : a movie called Avatar, dir. James Francis Cameron
3. Do Explicit Casting Optional to Any
λ€μ μ½λλ₯Ό 보μ. Optional μ κ·Έλλ‘ μ¬μ©νλ©΄ μλμ λμ§λ§ Swift λ μ΄λ₯Ό ν΄κ²°ν κ²μ κ²½κ³ νλ€.
let optionalNumber3: Int? = 3
things.append(optionalNumber3) // Warning: Expression implicitly coerced from 'Int?' to 'Any'
testAnyTypes(things)
Optional(3) : an integer value of 3
μ΄λ κ²½κ³ λ₯Ό μ κ±°νκΈ° μν΄ Nil-Coalescing Operator(??)
λ Forced Unwrapping(!)
μ μ¬μ©ν μ μλ€.
let optionalNumber5: Int? = 5
let optionalNumber7: Int? = 7
things.append(optionalNumber5 ?? 0)
things.append(optionalNumber7!)
testAnyTypes(things)
5 : an integer value of 5
7 : an integer value of 7
μ¬μ€ Error κ° μλ Warning μ΄λ―λ‘ μλμ λ¬Έμ λ μλ€. νμ§λ§ κ²½κ³ λ μ κ±°νκ³ , Unwrapping μ νμ§ μμ μ± Optional μνλ₯Ό μ μ§ν μλ μμκΉ?
Any
casting μ μ΄λ₯Ό κ°λ₯νκ² νλ€.
let optionalNumber9: Int? = 9
things.append(optionalNumber9 as Any)
testAnyTypes(things)
Optional(9) : an integer value of 9
Reference
- βType Casting.β The Swift Programming Language Swift 5.7. accessed Jan. 14, 2023, Swift Docs Chapter 18 - Type Casting.