ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Type Casting
    iOS/Swift 공식문서 2022. 2. 23. 15:29

    Type Casting

    인스턴스의 타입을 확인하거나, 인스턴스를 해당 클랫스 계층구조의 다른 superclass나 subclass로 취급하는 방법이다. Swift에서는 isas연산자로 구현되어 있다.


    Defining a Class Hierarchy for Type Casting

    타입 캐스팅에 대한 설명을 위해 우선 하나의 계층구조에 들어가는 클래스들을 정의하자. 3개의 클래스는 다음과 같다.

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

    그 다음으로 위의 클래스들의 인스턴스들을 담는 배열인 library를 선언하자. Movie, Song 클래스들은 MediaItem이라는 공통 superclass가 있기 때문에 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 배열에 내부에는 MovieSong의 인스턴스들이 있지만, 배열의 element들에 대하여 반복문을 수행하면, 우리가 되돌려받는 각 아이템은 MediaItem 타입을 가지게 된다. 인스턴스들의 원래 타입으로 작업을 하려면, 각 인스턴스의 타입을 확인하고 다른 타입으로 downcasting해야 한다.


    Checking Type

    인스턴스가 특정 subclass 타입의 인스턴스인지 확인하려면 is 연산자를 사용한다.

    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"

    Downcasting

    특정 클래스 타입의 상수/변수는 실제로는 subclass의 인스턴스를 참조할 수 있다. 이러한 경우, 타입 캐스팅 연산자(as? 혹은 as!)를 사용하여 해당 subclass 타입으로 downcasting할 수 있다.


    downcasting이 실패할 가능성도 있기 때문에, 타입 캐스팅 연산자는 2개의 형태로 정의된다. as? 연산자는 downcasting하려는 타입의 optional 값을 반환한다. as! 연산자는 downcasting을 시도하고 강제 unwrapping을 한다. downcasting이 성공할지 확신할 수 없다면 as?를 사용하자. as!의 경우 downcasting이 실패할 경우 런타입 에러를 발생시킨다.


    as? 연산자의 결과는 optional 값이기 때문에 optional binding과 같이 사용할 수 있다.

    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

    Type Casting for Any and AnyObject

    Swift는 불특정 타입들에 대하여 작업을 할 수 있도록 특별한 타입을 제공한다.


    a. Any는 함수 타입을 포함한 모든 타입의 인스턴스를 나타낼 수 있다.
    b. AnyObject는 모든 클래스 타입을 나타낼 수 있다.


    AnyAnyObject는 해당 타입들이 제공하는 기능이 분명히 필요한 경우에만 사용하자. 웬만하면 실제 작업할 타입을 구체적으로 정하는 것이 좋다.


    다음과 같이 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)" })

    아래와 같이 Any 타입의 컬렉션에서 작업을 할 경우 각 element의 타입을 확인하고 작업해야 한다.

    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

    note

    Any 타입은 optional 타입도 포함한다. Swift는 Any 타입의 값을 넣어야 하는 자리에 optional 값을 넣는 경우 경고를 한다. Any 값으로 반드시 optional 값을 사용해야 한다면 optional 타입을 Any로 변환하기 위해 as 연산자를 사용할 수 있다.

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

    Reference

    https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html

    'iOS > Swift 공식문서' 카테고리의 다른 글

    Extensions  (0) 2022.02.23
    Nested Types  (0) 2022.02.23
    Concurrency  (0) 2022.02.23
    Error Handling  (0) 2022.02.22
    Optional Chaining  (0) 2022.02.22

    댓글

Designed by Tistory.