티스토리 뷰

Swift

Swift ) Type Casting

Zedd0202 2017. 10. 23. 17:02
반응형

안녕하세요 :) Zedd입니다.

요새 조금 바빴어요 ㅠㅠ 이것저것 하느라 글을 못썼네요!!

지금 프로토콜글은....정말 왜 이런 예제를 넣었지..?라는 의문이 드는 예제가 빡 있어서 이걸 어떻게 해야할지 모르겠어요..

프로토콜글은 조금 늦게 올라갈 것 같습니다..!!! 흐규ㅠㅠㅜㅜ


그래서 오늘은 간단하게? Type Casting(타입 캐스팅) 에 대해서 알아보려고 해요!!!!



Type Casting



타입캐스팅은 인스턴의 타입을 확인하거나, 인스턴스의 타입을 슈퍼클래스 또는 서브클래스 타입처럼 다루기 위해 사용합니다.

Swift에서 타입 캐스팅은 "is"와 "as"라는 연산자로 구현할 수 있으며, 이 두 연산자는 값의 타입을 확인하거나, 값을 다른 타입으로 변환하는 간단하고 표현적인 방법을 제공합니다.




Defining a Class Hierarchy for Type Casting


클래스 및 하위 클래스의 계층 구조와 함께, 타입 캐스팅을 사용하여 특정 클래스 인스턴스의 타입을 확인하고, 해당 인스턴스를 동일한 계층구조 내의 다른 클래스로 캐스트(Cast)할 수 있습니다. 

흐음..예제를 읽어보니 좋은 예제 인 것 같아요. 대신 조금 깁니다. ㅎㅎ
Apple 예제도 진짜 좋아요! 근데 Swift를 처음 접하시는 분들?은 조금 어렵다고 느끼실 수 있으니.. 간단한 예제를 하나 보고 애플 예제를 같이 볼게요. 
우리 방금 " 타입 캐스팅을 사용하여 특정 클래스 인스턴스의 타입을 확인하고"라고 말했습니다.

네! 우리는 특정 클래스의 인스턴스의 타입을 정말 "확인" 할 수 있어요.
바로 "is"라는 키워드를 통해서 말이죠. (클래스라고 말은했지만, struct도 된답니다.)
자!! "is"를 한번 써봅시다. 

class Person {

    var name: String

    init(name: String) {

        self.name = name

    }

}

자. Person이라는 클래스를 정의해주었어요.

저장 인스턴스 프로퍼티로 name이 있네요. 

초기값이 없으므로 init을 만들어준 것을 볼 수 있습니다. 

var zedd = Person(name: "Zedd")

if zedd is Person{

    print(true)//true

}

자!!! is를 한번 써봤어요. 완전 확 와닿죠!!

Person타입의 인스턴스인 zedd를 하나 선언하고, 

이 "zedd"라는 인스턴스가 Person의 인스턴스냐? 라고 확인하는거죠.

이럴 때 is를 사용할 수 있답니다. 이렇게 인스턴스 자체를!! 확인 할 수도 있지만,

인스턴스의 프로퍼티도 당연히 확인할 수 있겠죠?

예제로 봅시다. 

var zedd = Person(name: "Zedd")

if zedd.name is String{

    print(true)//true

}

Person타입에는 name이라는 String타입 프로퍼티가 있었죠?

zedd.name. 즉 위에선 "Zedd"가 String타입이냐? 라고 확인하는거죠. 

String타입이 맞으니 true가 나오겠네요.


어때요!!

이제 Apple예제를 봐봅시다. 이번 글 끝까지 연결되는 예제들이니 잘 보셔야해요. 


class MediaItem {

    var name: String

    init(name: String) {

        self.name = name

    }

}

이렇게 MediaItem이라는 클래스를 정의했네요.. Person과 똑같죠..?ㅎㅎ


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을 상속받는!!!! 즉 MediaItem을 슈퍼클래스로 하는 Movie클래스와 Song클래스를 정의했어요. 


let library = [

    Movie(name: "죽은 시인의 사회", director: "피터 위어"),

    Song(name: "창공", artist: "김준석"),

    Movie(name: "인터스텔라", director: "크리스토퍼 놀란"),

    Movie(name: "공범자들", director: "최승호")

]

그리고 배열하나를 만듭니다. 이 안에는 방금 만든! MediaItem을 상속받는 Movie와 Song타입 인스턴스들을 넣어줍니다.

자연스럽게 library라는 배열은 MediaItem타입의 배열이 되게됩니다. 


자, 위에서 해본 "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 \(movieCount)개의 영화와  \(songCount)개의 노래가 있어요!")

길어보이지만 전혀!! 어렵지 않습니다. 저~~기 위에서 했던 예제랑 똑같아요.

library배열을 돌면서 해당 인스턴스가 Movie타입인지, Song타입인지 "is" 로 확인하고 있죠? 


출력은? 


Media library 3개의 영화와  1개의 노래가 있어요!



가 나오겠네요. ㅎㅎ


Downcasting


특정 클래스 타입의 상수 또는 변수는 하위 클래스의 인스턴스를 참조할 수 있습니다.
이 경우에는 타입 캐스트 연산자 (ex. as? 또는 as!)를 사용하여 서브 클래스 타입으로 "다운캐스팅"을 시도할 수 있습니다.

다운캐스팅은 실패할 수 있기 때문에, 타입 캐스트 연산자는 두가지 형태로 제공됩니다.
조건부 형식인 ?은 다운캐스팅 하려는 타입의 Optional 값을 반환합니다.
강제 형식인 !은 강제 언래핑을 하여 값을 반환합니다. 

그러니, 다운캐스팅이 항상 성공할 것이라는 확신이 들때만 강제형식인 !를 사용하세요.
이 형식은 잘못된 클래스 타입으로 다운캐스트 하려고 하면, 런타임 에러를 발생시킵니다. 

예제를 통해서 볼까요? 

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: 죽은 시인의 사회, dir. 피터 위어

Song: 창공, by 김준석

Movie: 인터스텔라, dir. 크리스토퍼 놀란

Movie: 공범자들, dir. 최승호

 */

if let구문을 활용한 것을 볼 수 있네요. 그리고 as? 라는 조건부 형식 다운캐스팅을 진행하고 있네요.

왜냐하면 library는 MediaItem타입 배열이고, 그 안에 들어있는 인스턴스는 MediaItem의 서브클래스들인 Movie와 Song이니까요.

역시 for문을 돌면서, 해당 인스턴스가 Movie로 다운캐스팅 될 수 있으면!!! print구문을 실행하네요.

해당 영화의 이름과 감독이름을 출력하도록요.


그리고 만약 해당 인스턴스가 Song타입으로 다운캐스팅 할 수 있으면 해당 노래의 이름과 아티스트 이름을 출력하네요. 

위 예제를 어렵게 생각하지마세요! MediaItem이 슈퍼클래스였고, 지금 Movie와 Song클래스는 MediaItem의 서브클래스(자식클래스)이므로, 다운캐스팅이 가능한 건 당연하겠죠? 그걸 as로 해주는거에요!!

item은 MediaItem인스턴스 이기때문에, Movie일수도 있고, Song일 수도 있는것이죠. 


그리고 as? 는 옵셔널 값을 반환하기 때문에, if let구문으로 값을 꺼내온 것을 볼 수 있습니다. 

그 꺼내온 값을 Movie타입으로 다운캐스팅할 수 있으면 movie에 넣고, Song타입으로 다운캐스팅 할 수 있으면 song에 넣는거죠.


Type Casting for Any and AnyObject


Swift는 특별한 타입을 제공합니다. Any와 AnyObject죠.
우리 이거 배웠었는데!!!!<Any와 AnyObject의 차이>를 읽고와주세요 :)

간단하게 요약하면, Any는 function타입을 포함한 모든 타입의 인스턴스를 나타낼 수 있고,
AnyObject는 모든 "클래스" 타입의 인스턴스를 나타낼 수 있었어요. 
하지만, 이 Any와 AnyObject는 이 두 타입이 제공하는 동작 및 기능이 필요한 경우만 사용하셔야 합니다.
코드에서 처리할 타입은 항상 구체적으로 기술하는 것이 좋기때문이죠. 



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: "대장 김창수", director: "이원태"))

things.append({ (name: String) -> String in "Hello, \(name)" })

Any는 모든 타입을 넣을 수 있다고 그랬죠? Int부터 클로져까지!!!!!넣을 수 있답니다. 와 클로져를 넣을 수 있다는것은 지금알았네요 ㄷㄷ

정리하면, things배열은 Any타입으로, 두개의 Int값, 두개의 Double값, String값, (Double, Double)인 튜플, 위에서 정의한 Movie타입, 그리고 문자열을 반환하는 클로져가 들어가있습니다.

이제 "as"를 써볼건데요, 문서에는 as에 대한 언급이 없네요.. as?와 as!만 있고..

as와 as? as! 의 차이점은 실행되는 시간의 차이가 있다네요. 

as는 컴파일타임에, as?와 as!는 런타임에 실행된다고 합니다. 

그리고, 업캐스팅과 패턴매칭(switch)에서만 사용할 수 있다고해요.  바로 예제처럼요!


for thing in things {

    switch thing {

    case 0 as Int:

        print("Int타입 0")

    case 0 as Double:

        print("Double 타입 0")

    case let someInt as Int:

        print("0 아닌 Int \(someInt)")

    case let someDouble as Double where someDouble > 0:

        print("양의 Double타입 \(someDouble)")

    case is Double:

        print("다른 Double들은 출력하고 싶지 않네,,")

    case let someString as String:

        print("String값은 \"\(someString)\"")

    case let (x, y) as (Double, Double):

        print("x y좌표는 \(x), \(y)")

    case let movie as Movie:

        print("영화 이름은 \(movie.name), dir. \(movie.director)")

    case let stringConverter as (String) -> String:

        print(stringConverter("미카엘"))

    default:

        print("다른거~~")

    }

}



자, 이렇게 오늘은 타입캐스팅에 대해서 알아봤어요 :)

도움이 되었으면 좋겠네요!!! 얼른 프로토콜 글도 쓰도록 할게요 XD..

안녕!!

반응형

'Swift' 카테고리의 다른 글

Swift) dynamic이란? / Realm의 dynamic var는?  (8) 2017.11.17
Swift ) Patterns  (0) 2017.10.31
Swift ) Protocols (2)  (3) 2017.10.18
Swift ) Method  (7) 2017.10.17
Swift ) Protocols (1)  (4) 2017.10.16