Notice
Recent Posts
Recent Comments
Link
«   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
Archives
Today
Total
관리 메뉴

욤찌의 개발 일기

[Swift] Any랑 AnyObject 공부했다. 근데 TypeCasting을 곁들인,,(1) 본문

Swift

[Swift] Any랑 AnyObject 공부했다. 근데 TypeCasting을 곁들인,,(1)

yyomzzi 2023. 11. 3. 10:02

오늘은 Any와 AnyObject를 배우다보니

타입캐스팅의 개념을 모르면 Any와 AnyObject 개념도 확실히 알 수가 없더라더라~~~

근데 평소에도 프로젝트를 하다보면 은근히 as를 쓰는데가 많았눈데

확실히 몰라서 '타입캐스팅 이고이거 꼭 나중에 봐야지!!!' 했는데 그 나중이 오늘이 되어버렸다눈,,

Any와 AnyObject 그리고 타입캐스팅까지 뽀샤봅시당

 


💡Any와 AnyObject에 대해 설명하시오.

 

아챠챠챠 그전에!! 타입캐스팅에 대해서 잠깐만 보고 갈께옹~~!~!

1) 타입 캐스팅이 뭐여뭐여

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 two operators provide a simple and expressive way to check the type of a value or cast a value to a different type. You can also use type casting to check whether a type conforms to a protocol.

1) 인스턴스의 타입을 확인하거나

2) 해당 인스턴스가 동일한 클래스 계층구조에 있는 슈퍼클래스나 서브클래스의 클래스 타입을 취급하는 방법

+) 타입 캐스팅을 사용해서 타입이 특정 프로토콜을 준수하는지도 확인 가능

 

결국 어떤 인스턴스의 타입이 무엇인지 확인하고, 해당 인스턴스의 타입을 자체 클래스의 슈퍼 클래스나 서브클래스의 타입을 가져와서 쓰는것을 뜻하나봄~~!~!

 

2) 인스턴스의 타입이 무엇인지 확인 => is 연산자

is 연산자는 타입을 체크하는 연산자로 타입에 대한 검사를 수행한다.

인스턴스의 타입이 뭔지 확인해보는 연산자임!

확인을하게 되면 당연히 결과를 알려주겠쥬? 그래서

The type check operator returns true if the instance is of that subclass type and false if it’s not.

해당 타입이거나 서브클래스일 경우에는 true를, 그게 아닐 경우에는 false를 반환해줌 (Bool 값 리턴)

 

공식문서에 있는 예제로 이것이 무엇인가 보자면

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


// MediaItem 상속
class Movie: MediaItem {
    var director: String
    init(name: String, director: String) {
        self.director = director
        super.init(name: name)
    }
}

// MediaItem 상속
class Song: MediaItem {
    var artist: String
    init(name: String, artist: String) {
        self.artist = artist
        super.init(name: name)
    }
}

 

MediaItem이라는 슈퍼클래스가 있고 상속받은 Movie, Song 이라는 서브클래스가 있움

MediaItem에는 name이라는 저장속성만 있고 상속받은 Movie에는 director가 추가되고 Song에는 artist가 추가됨

쉽죠잉

 

그렇다면 Movie 의 인스턴스를 하나 만들어서 변수에 담아보께옹

let movie1 = Movie(name: "친절한 금자씨", director: "박찬욱")

 

그리고 is 연산자를 통해서 movie의 타입을 체크해봅시다

movie1 is Movie // true

is는 이항연산자이기 때문에 위의 코드처럼 is 앞에는 확인하려는 변수명(표현식)을 위치시키고

is 뒤에는 타입명을 써주면 됨. 그러면 이것은 "movie1은 Movie 타입입니까?" 가 되는거임

그렇다면 너무너무 당연스럽게 true가 리턴되겠쥬?

 

근데 재밌는 것이 is 연산자는 returnstrueif the instance is of that subclass type 이라고 했음.  

그니까 인스턴스가 is 연산자로 확인할 타입의 서브클래스 인스턴스여도 true를 반환하는거임!

movie1 is MediaItem // true

바로 이러케!

movie1은 Movie 타입의 인스턴스이지만, MovieMediaItem을 상속받은 서브클래스이기 때문에

movie1은 MediaItem과의 타입 체킹에서도 true인거임~!

 

그렇다면 이것은?

// MediaItem의 상속을 받은 Movie의 상속을 받은 클래스

class AwardWinningMovie: Movie {
    var numberOfAwards: Int = 0
    init(name: String, director: String, numberOfAwards: Int) {
        self.numberOfAwards = numberOfAwards
        super.init(name: name, director: director)
    }
}

Movie의 상속을 받은 AwardWinningMovie라는 클래스가 있다고 가정해보자

 

그러면 movie1은 위의 클래스 타입으로 체킹을 해도 true를 반환할까?

movie1 is AwardWinningMovie // false

 

아~~니~~요~~~~

왜냐면 Movie 타입은 AwardWinningMovie 의 슈퍼클래스이쟈나욧

그래서 false를 반환함

AwardWinningMovieMovie 타입이 갖고있지 않은 멤버를 가지고 있음. 

 

 

밑에 또 재밌는 예시를 가져와보겠음

let library = [
    Movie(name: "친절한 금자씨", director: "박찬욱"),
    Song(name: "고양이", artist: "선우정아"),
    Movie(name: "기생충", director: "봉준호"),
    Song(name: "빈집", artist: "기리보이"),
    Movie(name: "헤어질 결심", director: "박찬욱")
]

그리고 MovieSong의 인스턴스들을 생성해서 library라는 배열에 모두모두 모아 담아버렸읍니다,,

 

이 뒤죽박죽쓰 배열에서 Movie 클래스와 Song 클래스의 인스턴스가 각각 몇개가 있는지 확인해보려고 함.

이럴때 is 연산자를 사용해서 누가 Movie 출신인지!! Song 출신인지!! 알 수 있찌 않겠어유?

var movieCount = 0
var songCount = 0

⭐️
for item in library {
    if item is Movie {
        movieCount += 1
    } else if item is Song {
        songCount += 1
    }
}
⭐️

print("library 속 영화는 \(movieCount)편, 노래는 \(songCount)곡입니다.")

 

library 배열을 for-in 구문을 통해 돌려돌려 돌림판을 해서 item들의 타입을 is 연산자를 통해 확인해주는 작업임

위의 예시에서 보자면 library의 item들 중 Movie의 인스턴스라면 movieCount가 +1이 되고

Song 의 인스턴스라면 songCount가 +1이 될것이당

그래서 그 두 변수의 값을 확인하면 우뚜케 되겠움?

당연하게 영화는 3편, 노래는 2곡이라는 결과가 나올것임! Movie 인스턴스가 3개이고 Song 인스턴스가 2개이니까~

 

자 근데 생각해보니까,,,,,,

보자보자,,,,,,,

let library = [
    Movie(name: "친절한 금자씨", director: "박찬욱"),
    Song(name: "고양이", artist: "선우정아"),
    Movie(name: "기생충", director: "봉준호"),
    Song(name: "빈집", artist: "기리보이"),
    Movie(name: "헤어질 결심", director: "박찬욱")
]

 

이 배열 뭔가 넘 이상하지 않움?

어떻게 MovieSong은 다른 클래스 타입인데 이렇게 한 배열에 같이 있을 수 있는거지?????????

자,, 여기서 이제 타입캐스팅의 두번째 의미가 나옵니다

 

3) 인스턴스가 자체 클래스 계층구조에 있는 슈퍼클래스나 서브클래스의 타입을 취급하는 방법

=> 업캐스팅 / 다운캐스팅 (as / as! / as? 연산자)

 

3-1) 업캐스팅(Upcasting)

이것이 바로 업캐스팅임.

MovieSong은 다른 클래스이지만 library 배열에 담기는 순간 이 둘의 공통적인 슈퍼클래스인

MediaItem 타입으로 업캐스팅해서 배열에 담겼기 때문에 에러가 발생하지 않는거임!

그래서 library의 타입을 확인하면

[MediaItem]인것을 확인할 수 있음!

 

그래서 업캐스팅은 바로 동일한 클래스 계층 내에서 서브클래스의 메모리구조를 가진 인스턴스(ex: Movie 인스턴스)가

슈퍼클래스 타입(ex: MediaItem)으로 인식되는 것

library의 3개의 Movie 인스턴스들은 정의될 때 Movie 타입으로 정의되었기 때문에

힙 영역의 메모리구조는 Movie 클래스로 저장이 되었을 것이다.

하지만 library 배열에 담기는 순간에 그 슈퍼클래스인 MediaItem으로 업캐스팅되어 인식되었음.

 

업캐스팅은 as 연산자를 사용함.

그리고 사실 업캐스팅은 항 상 무 조 건 성공임.

서브클래스 안에는 슈퍼클래스를 포함하고 있으니까(상속 관계) 당연히 슈퍼클래스의 속성을 가지고 있기 때문에

슈퍼클래스로 추론이 가능한거임! 

 

그러면 슈퍼클래스가 서브클래스로도 인식될 수 있음? 

그것이 바로 다운캐스팅임

 

3-2) 다운캐스팅(Downcasting)

다운캐스팅은 업캐스팅과 반대로 슈퍼클래스 타입의 인스턴스를 서브클래스로 인식하는 것을 말함

아까 봤던 library 배열을 다시 보자면 

let library: [MediaItem] = [
    Movie(name: "친절한 금자씨", director: "박찬욱"),
    Song(name: "고양이", artist: "선우정아"),
    Movie(name: "기생충", director: "봉준호"),
    Song(name: "빈집", artist: "기리보이"),
    Movie(name: "헤어질 결심", director: "박찬욱")
]

업캐스팅으로 인해서 library의 타입은 [MediaItem]이 되었다

그래서 3개의 Movie 인스턴스와 2개의 Song 인스턴스는 현재 MediaItem 타입이다.

근데 사실! 이렇게 되면 아무리 Movie에 name과 director를 가지고 있어도 director에는 접근이 불가함

 

왜냐구?

메모리 구조는 Movie일지언정,, 현재 이 친구는 MediaItem으로 인식되어있기 때문에!

MediaItem의 속성밖에 접근할 수 없는거임 ㅠ

 

속성에 접근하려고 해도 name 프로퍼티만 접근할 수 있어유,,

근데 그러면 이제 더이상은 Movie로 살아갈 수 없는거임 ?ㅠㅠㅠㅠㅠ

그래서 이럴때 다운캐스팅을 해주면 됨!!

 

let movie1: MediaItem = Movie(name: "친절한 금자씨", director: "박찬욱")

if let movie2 = movie1 as? Movie {
    print("영화 제목: \(movie2.name), 감독: \(movie2.director)")
}

// Prints "영화 제목: 친절한 금자씨, 감독: 박찬욱"

여기서 보면 특이하게 보이는 하나가 있쥬?

바로 as? 연산자 입니다

근데 뒤에 물음표가 붙었네? 그것은 바로 옵셔널로 값이 나올 수 있다! 즉, 실패가능성이 있다는 말임

 

오잉? 왜 실패하쥬?

 

MediaItemMovie 클래스를 예로 들어 메모리 구조를 보자면, 

Movie 클래스의 인스턴스를 생성할 때, 메모리에는 당연히 Movie 클래스의 메모리구조를 가지고 저장할 것이고

스택에는 그 힙 영역에 주소를 저장할 것이다.

그래서 그 주소를 참조하는 변수(예를들면, 아까 movie1과 같은)를 스택에 저장할텐데,

그 변수가 MediaItem 타입으로 업캐스팅 되어서 저장된거임.

그래서 Movie 클래스로 메모리구조를 가지고 있어도 MediaItem으로 인식되기 때문에 접근에 제한이 있음. 

 

그럼 반대로 MediaItemMovie로 다운캐스팅 될 때는 MediaItem의 인스턴스가

실 제 로 는 Movie의 메모리 구조를 가지고 있어야 함!

그래야 Movie로 다운캐스팅을 할 때 Movie 메모리에 모두 접근이 가능할테니까!

근데 만약 서브클래스의 메모리 구조를 가지고 있지 않은 슈퍼클래스의 인스턴스라면...?

당연히 다운캐스팅이 실패하겠쥬?

 

근데 .. 말이 되나..? 서브클래스의 메모리 구조를 다 가지고 있는 슈퍼클래스의 인스턴스가 어디있땀?

 

그래서 여기서 알 수 있 는 다운캐스팅의 전제 조건은 바로 !!!! 업캐스팅이 되었던 인스턴스에 가능하다는 것!

서브클래스의 메모리 구조를 가지고 한 번 정의 되었던 인스턴스가

슈퍼클래스로 업캐스팅이 되었을 때만 서브클래스의 메모리 구조를 가질 수 있겠쥬? 

 

다운캐스팅은 실패가능성이 있기 때문에 as? 로 선언해서

다운캐스팅이 성공한다고 해도 무조건 옵셔널 값을 반환함. (실패하면 nil을 반환)

그렇기 때문에 옵셔널을 벗겨서 사용해야함. 

위의 예시에서도 if let 으로 옵셔널 값을 언래핑해서 사용하는 것을 볼 수 있음. 

if let movie2 = movie1 as? Movie {
    print("영화 제목: \(movie2.name), 감독: \(movie2.director)")
}

 

근데 만약? 이건 진짜 확실하게!!! 다운캐스팅이 될 것이라고 내가 확신해 !!!!! 한다면

as! 연산자 로 강제 언래핑해서 사용할 수 있음 (근데 강제 언래핑은 언제나 조심할 것,,,)

 

자!!!!! 다시!!!!!! library 배열 예시로 보자면

3개의 Movie 클래스 인스턴스와 2개의 Song 클래스 인스턴스들은 library 배열에 들어가면서 업캐스팅이 된 상태임

그러면 이제 다운캐스팅을 해보겠슴다

for item in library {
    if let movie = item as? Movie {
        print("영화 제목: \(movie.name), 감독: \(movie.director)")
    } else if let song = item as? Song {
        print("노래 제목: \(song.name), 가수: \(song.artist)")
    }
}

library를 for-in 구문으로 돌려돌려 돌림판을 해서 나온 item들을 다운캐스팅을 하면 결과는 이렇게 나옵니댜

 

 

 

근데 업캐스팅하고 다운캐스팅하고 그러면 ,, 클래스라서 참조타입인데 원래 인스턴스가 막 바뀌거나 하는거 아녀? 

했었눈데 공식문서에서 이렇게 안심시켜줍니다

Casting doesn’t actually modify the instance or change its values. The underlying instance remains the same; it’s simply treated and accessed as an instance of the type to which it has been cast.

인스턴스는 동일하게 유지 되지만 캐스팅을 통해 그냥 캐스트된 타입으로 인식해서 접근만 달라지는 것이라고 합니다~

 

 


Any와 AnyObject 공부하려고 했다가 결국 Any는 언급도 못한 블로그 ,,, 

타입 캐스팅 하다보니까 글 넘 길어져서 다음 글에서 쭈절쭈절하겠슴다 

타입 캐스팅 만만하게 봤는데 한개도 안만만하고 어렵네 ..^^ 그래도 배웠으니 되었다~

 

 

📖 reference(늘 감사합니당) ♥️

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/typecasting/#Checking-Type

 

Documentation

 

docs.swift.org

https://velog.io/@din0121/Upcasting-Downcasting

 

[Swift] - Upcasting & Downcasting

안녕하세요:)오늘은 인스턴스를 동일한 클래스 계층에 존재하는 다른 클래스 형식으로 처리하는 방법에 대해서 알아보도록 하겠습니다.동일한 클래스 계층에서 수행되는 Upcasting은 안전하고 항

velog.io

https://babbab2.tistory.com/127

 

Swift) is, as - 타입 캐스팅 (Type Casting)

안녕하세요, 소들입니다!!!! 오늘 포스팅은 is와 as 즉, 타입 캐스팅에 대해 알아보려고 해요 :D 타입 캐스팅,,, 개발 하다보면 가끔 보이는.. is as as? as! 막 ... 이렇게... 단어 하나갖고 장난질이여;;;

babbab2.tistory.com