티스토리 뷰

Swift

Swift [Type Casting(is, as)]

이도형 2023. 2. 23. 23:58

INDEX

1.  타입 캐스팅(Type Casting)

2.  is연산자

3. ⭐️ as연산자⭐️

4.  Bridging

5.  마무리

 

1.  타입 캐스팅(Type Casting)

 

타입캐스팅(Type Casting)이란 인스턴스의 타입을 확인(is)하거나, 해당 인스턴스를 해당 인스턴스의 클래스내의 상위클래스나, 하위클래스로 취급(as?)받을 수 있는 방법을 의미한다.

타입캐스팅은 실제로 인스턴스를 수정하거나 내부 속성값을 변경하는게 아니라(인스턴스 내부 속성 자체는 불변)

컴퓨터에게 어떤 타입으로 인스턴스에게 접근할지를 명령하는 것이다. => (as연산자까지 보면 무슨말인지 알 것이다.)

Swift에서 타입캐스팅은 is연산자와 as연산자로 구현된다.

is연산자를 통해 인스턴스의 타입을 확인하고, as연산자를 통해 상위클래스나 하위클래스로 취급받을 수 있다.

타입 캐스팅은 타입이 프로토콜에 적합한지 여부를 체크할때에 사용되며, 실제로 앱을 만들때에도 정말 많이 사용된다. 

 

is와 as를 공부하기전에 먼저 상속에 관한 리마인드가 필요하다.

상속은 클래스에서만 갖는 기능이며 상속을 통해 데이터(저장 속성)를 받기도 하지만, 데이터의 추가가 가능하였다.

이에 대해 야곰님의 도서에서 잘 설명해 놓았고 아래가 그 모델이다.

 

Coffee의 상속을 받는 Latte와 Americano

 

Latte와 Americano는 Coffee의 특성을 모두! 포함함

위 모델에서 Latte와 Americano는 Coffee를 상속해 모든 특성을 갖기에 Coffee인척하는데 문제가 없다(당연한 말)

하지만 Coffee가 Latte인척을하고, Americano인척을 하기에는 문제가 발생한다. (Latte만이 갖는 특성, Americano만이 갖는 특성)

 여기까지 이해한 상태라면 is와 as연산자를 이해하기 매우 쉬워진다.

 

 

2. is: Type Checker

is연산자는 인스턴스 타입을 검사하는 Type Checker Operator

문법 => 인스턴스 is 타입

이항 연산자로 왼쪽엔 인스턴스를 적고, 오른쪽엔 타입을 적으면된다.

리턴타입은 Bool

 

매우 쉬운 개념이므로 간단한 예시코드만 작성하고 as로 넘어간다.

//타입 체크하기
let a: Int = 3
a is Int //true

let b = "bbbb"
b is Character //false

//상속 계층의 관계에서 포함관계 생각하기

class Person {
    var id = 0
    var name = "이름"
    var email = "abc@gmail.com"
}

class Student: Person {
    // id
    // name
    // email
    var studentId = 1
}

class Undergraduate: Student {
    // id
    // name
    // email
    // studentId
    var major = "전공"
}

let person1 = Person()

// 사람 인스턴스는 학생/대학생 타입은 아니다. (사람 타입이다.)
person1 is Person                // true
person1 is Student               // false
person1 is Undergraduate         // false

let student1 = Student()
// 학생 인스턴스는 대학생 타입은 아니다.  (사람/학생 타입니다.)
student1 is Person               // true
student1 is Student              // true
student1 is Undergraduate        // false

let undergraduate1 = Undergraduate()
// 대학생 인스턴스는 사람이거나, 학생이거나, 대학생 타입 모두에 해당한다.
undergraduate1 is Person         // true
undergraduate1 is Student        // true
undergraduate1 is Undergraduate  // true

// 예제를 통한 활용
let person2 = Person()
let student2 = Student()
let undergraduate2 = Undergraduate()


let people = [person1, person2, student1, student2, undergraduate1, undergraduate2]

// 학생 인스턴스의 갯수를 세고 싶다.

var studentNumber = 0

for someOne in people {
    if someOne is Student {
        studentNumber += 1
    }
}

print(studentNumber) //4

 

 

2.  as: Type Casting

 

1. Upcasting(업캐스팅)

문법 => 인스턴스 as 타입

하위클래스의 메모리구조로 저장된 인스턴스를 상위클래스 타입으로 인식한다.

항상 성공한다.

class Person {
    var id = 0
    var name = "이름"
    var email = "abc@gmail.com"
}


class Student: Person {
    // id
    // name
    // email
    var studentId = 1
}


class Undergraduate: Student {
    // id
    // name
    // email
    // studentId
    var major = "전공"
}

let undergraduate2: Undergraduate = Undergraduate()
undergraduate2.id //0
undergraduate2.name //이름
undergraduate2.studentId //1
undergraduate2.major //전공
undergraduate2.name = "길동"

let person4 = undergraduate2 as Person       // 항상 성공 (컴파일러가 항상 성공할 수 밖에 없다는 것을 알고 있음)
person4.id //0
person4.name //길동
//person4.studentId //업캐스팅으로 접근불가. Value of type 'Person' has no member 'studentId'
//person4.major //업캐스팅으로 접근불가 Value of type 'Person' has no member 'major'

 

2.Downcasting(다운캐스팅)

상위클래스에서 하위클래스 타입으로 인식하고자 하는 것으로  (ex.Coffee가 Americano로 인식되고자 하는 걸) 뜻한다.

실패가능성이 있으며 as?와 as!가 존재한다.

 

as?

문법 => 인스턴스 as? 타입

반환타입은 Optional타입이므로 실패시 nil 반환

 

as!

문법 => 인스턴스 as! 타입

참이면 반환타입은 Optional타입의 값을 강제 언래핑한 타입

실패시 런타임 오류(다운캐스팅이 반드시 성공한다는걸 확신할때만 사용)

 

위의 타입캐스팅에 대한 설명에서 타입캐스팅이란 인스턴스를 수정하는게 아니라, 

어떤 타입으로 인스턴스를 접근하면 될지 컴퓨터에게 알려주는 것이라 하였다.

아래 예제들을 보자

 

예제 1 - 상위클래스의 타입정의로 인스턴스의 속성 접근 막기

class Person {
    var id = 0
    var name = "이름"
    var email = "abc@gmail.com"
}


class Student: Person {
    // id
    // name
    // email
    var studentId = 1
}


class Undergraduate: Student {
    // id
    // name
    // email
    // studentId
    var major = "전공"
}
let person: Person = Undergraduate()
person.id
person.name
person.email
//person.studentId    // Value of type 'Person' has no member 'studentId'
//person.major          // Value of type 'Person' has no member 'major'

// 그런데, 왜 studentId 와 major 속성에는 접근이 되지 않을까? ⭐️

// person2변수에는 Person타입이 들어있다고 인식
// ===> 접근하고 싶다면 메모리구조에 대해 컴퓨터에게 힌트 알려줄(타입 캐스팅) 필요

 

예제 2 - as?로 다운캐스팅하여 접근 범위 되찾기

class Person {
    var id = 0
    var name = "이름"
    var email = "abc@gmail.com"
}


class Student: Person {
    // id
    // name
    // email
    var studentId = 1
}


class Undergraduate: Student {
    // id
    // name
    // email
    // studentId
    var major = "전공"
}

let person: Person = Undergraduate()

let ppp = person as? Undergraduate  // Undergraduate? 타입

if let newPerson = person as? Undergraduate {   // if let 바인딩과 함께 사용 (옵셔널 언래핑)
    newPerson.major
    print(newPerson.major)
}
//Prints 전공

// 실제로 인스턴스의 접근 범위를 늘려주는 것 뿐임

//Person타입의 person객체를 as!으로 다운캐스팅하여(강제 언래핑) person3에 대입
let person3: Undergraduate = person as! Undergraduate
person3.major //전공

예제3 - Optional to Non-Optional 다운 캐스팅 예제

let bigger: Int? = 3
let big: Int
big = bigger as! Int //Optional타입을 Non-Optional타입 Int로 강제 다운캐스팅하여 대입
print(big) //3

예제4 - 🍎공식문서 예제로 타입 캐스팅 정리

//기본 클래스 - 미디어 라이브러리로 모든 미디어(영화,노래,사진등) 포함
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)
    }
}


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")
]

type(of: library) //[MediaItem]
// the type of "library" is inferred to be [MediaItem]


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"


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

 

4.  브릿징(Bridging)

Bridging (브릿징)

스위프트에서 브릿징이란 서로 호환되는 형식을 캐스팅해서 쉽게 사용하는 것으로

스위프트가 나오기 전까지 사용되었던 objective-C로 작성된 데이터와 서로 교환하기 위해 만들어졌다.

스위프트에서는 내부적으로 여전히 Objective-C의 프레임워크를 사용하는 것이 많기 때문에

서로 완전히 상호 호환이 가능하도록 설계해놓았음(completely interchangeable)

타입 캐스팅을 강제(as!)할 필요 없이 업캐스팅(as)만으로도 이루어진다

 

let str: String = "Hello"
let otherStr = str as NSString //Hello

 

5.  마무리

타입 캐스팅(Type Casting)은 인스턴스를 실제로 수정하는게 아니라 컴퓨터에게 인스턴스 타입의 접근범위에 대한 힌트를 주는 것

is연산자는 타입 체크(Type Checker) 연산자라는것

as연산자(Type Casting)에는 as(Up Casting)과 다운캐스팅 (Down Casting)이 있었다.

업캐스팅 as는 하위에서 상위타입으로 캐스팅되어 무조건 성공하며 인스턴스의 접근범위는 줄어들었고,

다운캐스팅 as?와 as!는 상위에서 하위타입으로 캐스팅되어 실패가능성이 있었고 인스턴스의 접근범위는 늘어났다.

as?는 조건부 다운 캐스팅 as!는 강제 다운 캐스팅

마지막으로 업캐스팅만으로 이루어지는 objective-c와의 브릿징(bridging)

Any와 AnyObject까지 한번에 다루려다가 다른 개념들과 함께 다루기 위해 다음 포스팅으로 미루었다.

타입캐스팅에서 이어지는 개념이므로 여기를 제대로 이해했다면 매우 쉬울 것이다.

 

출처

Inflearn Allen님의 강의 노트

Swift 공식문서

야곰님의 스위프트 프로그래밍 3판

 

 

 

 

 

 

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함