티스토리 뷰

 

 

Type Casting — The Swift Programming Language (Swift 5.5)

Type Casting 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. Type casting in Swift is implemented with the is and as operators. These tw

docs.swift.org

1차 수정 (2022 / 02 / 26) : 의역, 오타 및 스타일 수정

 

 

 

Type Casting

타입 캐스팅(Type Casting) 은 인스턴스의 타입을 확인하는 방식입니다. 혹은 인스턴스를 해당 클래스의 계층 구조에서 다른 슈퍼클래스나 서브클래스로 다룹니다.

 

Swift의 타입 캐스팅은 is 와 as 연산자로 구현합니다. 이 두 연산자는 `값의 타입을 확인`하거나 `값을 다른 값으로 캐스팅`하는 간단하고 효과적인 방법을 제공합니다. 

타입이 프로토콜을 따르는지 확인하기 위해서도 타입 캐스팅을 사용할 수 있습니다.

 

 

 

 

Defining a Class Hierarchy for Type Casting (타입 캐스팅 설명을 위한 클래스 계층 구조 정의)

클래스 및 서브클래스의 계층구조에 타입 캐스팅을 사용할 수 있습니다. 이를 사용해 특정 클래스의 타입을 확인하고 

해당 인스턴스를 같은 계층 구조의 다른 클래스로 캐스팅합니다. 

챕터에서 타입 캐스팅을 설명하기 위해 3개의 코드 예제를 작성합니다. 아래의 3개의 코드 예제는 클래스의 계층구조와 해당 클래스들의 인스턴스를 포함하는 배열을 정의합니다.

 

첫 번째로 MediaItem 클래스를 정의합니다. String 타입의 name 프로퍼티와, name 을 초기화하는 이니셜라이져를 가집니다.

class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}

 

두 번째로 MediaItem의 서브클래스로 Media 와 Song 클래스를 정의합니다.

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)
    }
}

 

세 번째로 library라는 배열을 만듭니다. library 배열은 2개의 Movie 인스턴스와 3개의 Song 인스턴스를 가집니다. 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]

library에 저장된 항목은 백그라운드에서는 여전히 Movie와 Song 인스턴스입니다. 그러나 배열의 내용을 반복(iterate) 하면, 리턴 받는 항목의 타입이 MediaItem이 됩니다. 이들을 원래 타입으로 작업하기 위해 타입을 확인하거나 다른 타입으로 다운캐스트(downcast) 합니다.

 

 

 

 

 

Checking Type (타입 확인)

인스턴스가 특정 서브클래스 타입인지 확인하기 위해, 타입 확인 연산자(type check operator) 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"

movieCount 와 songCount 변수를 정의합니다. 변수로 library 배열에 있는 Movie와 Song 인스턴스 갯수를 셉니다. (item is Movie) 는 만약 현재 MediaItem이 Movie 인스턴스이면 true를 리턴하고 아니면 false를 리턴합니다. 비슷한 방식으로 (item is Song) 도 Song 인스턴스를 확인합니다. fon-in 루프가 끝나면 movieCount 와 songCount 는 각각의 타입 갯수를 가집니다.

각각 2개, 3개의 인스턴스를 가집니다.

 

 

 

 

 

Downcasting (다운캐스팅)

특정 클래스 타입의 상수 및 변수는 아마 백그라운드에서 서브클래스의 인스터스를 실제로 참조합니다. 이런 경우가 있다면, 타입 캐스트 연산자(type cast operator) as? 및 as! 를 사용해 서브클래스를 다운캐스트(downcast) 할 수 있습니다.

 

다운캐스팅이 실패할 수 있기 때문에, 타입 캐스트 연산자는 두가지 다른 형식을 가집니다. 조건부(conditional) 형식 as? 는 다운캐스팅할 타입의 옵셔널 값을 리턴합니다. 강제(forced) 타입 as! 는 다운캐스트를 시도하고 단일 복합 작업(single compound action)으로 결과를 강제-언래핑 합니다.

 

다운캐스트의 성공을 확신할 수 없다면, as? 를 사용합니다. 이 형식의 연산자는 항상 옵셔널 값을 리턴할 것입니다. 다운캐스트가 가능하지 않다면 값은 nil을 리턴할 것입니다. 이를 통해 다운캐스트의 성공을 확인할 수 있습니다.

 

다운캐스트의 성공을 항상 확신할 수 있다면, as! 를 사용합니다. 잘못된 클래스 타입으로 다운캐스트를 시도하면 연산자는 런타임에러를 발생시킵니다.

 

아래 예제는 library의 각 MediaItem을 반복하고 각 항목에 대한 적당한 설명을 출력합니다. 이것을 하기 위해 단지MediaItem에 접근하는게 각 항목을 진짜 Movie 및 Song으로 접근해야 합니다. 이것은 Movie 및 Song의 프로피티에 접근하기 위해서 필요합니다.

 

예제에서 배열 안의 각 항목들은 아마 Movie 나 Song 일 것입니다. 각 항목에 대해 어떤 클래스를 사용하는지 모릅니다. 따라서 for루프 안에 조건부 형식 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로 다운캐스트 시도합니다. item이 MediaItem 인스턴스이기 때문에, Movie 이거나 Song 이거나 그냥 MediaItem 일 수 있습니다. 이러한 불확실성 때문에 as?로 서브클래스에 접근하려고 하면 옵셔널 값을 리턴합니다. (item as? Movie) 의 리턴 타입은 Movie? 혹은 옵셔널 Movie 입니다.

 

Song 인스턴스라면 Movie로의 다운캐스팅은 실패합니다. 이를 위해 위 예제에서는 옵셔널 Movie가 실제로 값을 포함하는지 옵셔널 바인딩을 사용해 확인합니다. 이 옵셔널 바인딩은 "if let movie = item as? Movie" 로 작성됐습니다.

 

"항목을 Movie로써 접근해보세요. 만약 성공한다면, 리턴된 옵셔널 Movie에 저장된 값으로 새로운 일시 상수 movie를 설정하십시오"

 

만약 다운캐스팅이 성공하면, movie의 프로퍼티들은 Movie를 설명하기위해 출력됩니다. 비슷한 방법이 Song 인스턴스를 확인하기 위해 사용됩니다.

 

캐스팅은 실제로 인스턴스를 수정하거나 그 값을 변경시키지 않습니다. 기본 인스턴스는 그대로 유지됩니다. 단순히 캐스팅된 타입의 인스턴스로 취급되고 접근됩니다.

 

 

 

 

Type Casting for Any and AnyObject (Any 및 AnyObject 에 대한 타입 캐스팅)

Swift는 `특정 타입이 아닌 타입`으로 작업하기 위해 특변한 타입 두 가지를 제공합니다.

  • Any는 함수를 포함해 모든 타입의 인스턴스를 나타낼 수 있습니다.
  • AnyObject는 모튼 클래스 타입의 인스턴스를 나타낼 수 있습니다.

동작과 기능이 명시적으로 필요할 때, 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)" })

things 배열은 2개의 Int 값, 2개의 Double 값, 1개의 String 값, 1개의 (Double,Double) 튜플 타입, Movie 타입, String 값을 받아 다른 String 값을 리턴하는 클로져를 가집니다.

 

`Any 및 AnyObject 타입`으로만 알려진 `상수 및 변수의 특정 타입`을 알기위해, Switch 문의 case에 is 와 as 를 사용할 수 있습니다.

아래 예제는 things 배열의 항목을 반복하고 각 항목의 타입을 Switch문을 통해 요청합니다. 일부 Switch 문의 case 는 지정된 타입의 상수에 일치하는 값을 묶어 값을 인쇄할 수 있습니다.

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

 

Any 타입은 옵셔널 타입을 포함한 모든 타입의 값을 나타냅니다. 만약 Any 타입의 값이 예상되는 곳에 옵셔널 값을 사용하면, Swift는 사용자에게 경고합니다. 만약 정말로 옵셔널 값을 Any 값으로 사용해야 한다면, 옵셔널을 Any로 명시적으로 캐스팅하기 위해 as 연산자를 사용할 수 있습니다.

let optionalNumber: Int? = 3
things.append(optionalNumber)        // Warning
things.append(optionalNumber as Any) // No warning

 

 

 

타입 캐스팅이라고 해서 C언어처럼 (int) 하는 내용을 다룰 줄 알았는데 is 와 as 연산자를 사용해 특정 클래스 타입이 맞는지 확인하는 내용이다.

 

 

 

타입캐스팅 끗

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함