이 포스팅은 Swift 시리즈 20 편 중 7 번째 글 입니다.

  • Part 1 - 01: Optional, Any, AnyObject, nil
  • Part 2 - 02: struct, class, enum
  • Part 3 - 03: Closure
  • Part 4 - 04: Property
  • Part 5 - 05: 상속, 생성자, 소멸자
  • Part 6 - 06: 옵셔널 체이닝과 nil 병합
  • Part 7 - This Post
  • Part 8 - 08: assert, guard
  • Part 9 - 09: Protocol
  • Part 10 - 10: Extention
  • Part 11 - 11: 오류처리
  • Part 12 - 12: 고차함수
  • Part 13 - 13: ARC(Automatic Reference Counting)
  • Part 14 - 14: Access control, Access modifier
  • Part 15 - 15: Generics
  • Part 16 - 16: Optional에 대한 깊은 이해
  • Part 17 - 17: Lazy Variables
  • Part 18 - 18: Enumeration
  • Part 19 - 19: Initialization
  • Part 20 - 20: Concurrency
▼ 목록 보기
  • 인스턴스의 타입을 확인하는 용도
  • 클래스의 인스턴스를 부모 또는 자식 클래스의 타입으로 사용할 수 있는지 확인하는 용도 (~? 원래 타입 캐스팅이라는 뜻이 그거였나~)
  • is, as를 사용함

기존의 타입 캐스팅

let someInt: Int = 100
let someDouble: Double = Double(someInt)

기존에 내가 알고 있던 타입 캐스팅은 이런 것이었다. 그런데 스위프트에서는 이건 타입 캐스팅이 아니다..! 이건 Double type의 Instance를 하나 새로 생성을 해주는 것. 기존에는 컴파일러가 형변환을 감지하고 처리를 했다.

imageDouble 생성자들

실제로 Double을 타고 들어가보니 생성자가 무지하게 많았다.

imageint에 대한 생성자

그리고 요로코롬 int에 대한 생성자가 정의되어 있었다.

스위프트의 타입 캐스팅

  • 클래스 인스턴스에 주로 많이 사용
  • Dictionary에서도 많이 활용
  • 굉장히 중요함

예제를 통해 알아보자.

class Person {
    var name: String = ""
    func breath() {
        print("숨을 쉽니다.")
    }
}

class Student: Person {
    var school: String = ""
    func goToSchool() {
        print("등교를 합니다.")
    }
}

class UniversityStudent: Student {
    var major: String = ""
    func goToMT() {
        print("멤버쉽 트레이닝을 갑니다!")
    }
}

// 인스턴스 생성
var wansik: Person = Person()
var hana: Student = Student()
var jason: UniversityStudent = UniversityStudent()

타입 확인

  • is 사용
wansik is Person // true
wansik is Student // false
wansik is UniversityStudent // false

hana is Person // true
hana is Student // true
hana is UniversityStudent // false

jason is Person // true
jason is Student // true
jason is UniversityStudent // true
if yagom is UniversityStudent {
    print("yagom은 대학생입니다")
} else if yagom is Student {
    print("yagom은 학생입니다")
} else if yagom is Person {
    print("yagom은 사람입니다")
} // yagom은 사람입니다

switch jason {
case is Person:
    print("jason은 사람입니다")
case is Student:
    print("jason은 학생입니다")
case is UniversityStudent:
    print("jason은 대학생입니다")
default:
    print("jason은 사람도, 학생도, 대학생도 아닙니다")
} // jason은 사람입니다

switch jason {
case is UniversityStudent:
    print("jason은 대학생입니다")
case is Student:
    print("jason은 학생입니다")
case is Person:
    print("jason은 사람입니다")
default:
    print("jason은 사람도, 학생도, 대학생도 아닙니다")
} // jason은 대학생입니다

이렇게 인스턴스의 타입을 확인하는데 사용할 수 있다. 그런데 문제는 두번째 switch 문을 살펴보게 되면, 부모 클래스의 타입을 먼저 확인하는 경우 해당 클래스로 타입이 추정되어, 더 세분화된 자식 클래스를 판단할 수 없다.

업 캐스팅

  • as를 사용하여 부모 클래스의 인스턴스로 사용할 수 있도록 컴파일러에게 타입정보를 전환해줌
  • 즉 자식 클래스를 부모 클래스의 인스턴스로 변환하는 것을 말함
  • Any, AnyObject로도 변환할 수 가 있다.
  • 암시적으로 처리되기 때문에 필요한 경우가 아니면 생략
// UniversityStudent 인스턴스를 생성하여 Person 행세를 할 수 있도록 업 캐스팅
var mike: Person = UniversityStudent() as Person

var jenny: Student = Student()
//var jina: UniversityStudent = Person() as UniversityStudent // 컴파일 오류

// UniversityStudent 인스턴스를 생성하여 Any 행세를 할 수 있도록 업 캐스팅
var jina: Any = Person() // as Any 생략가능

다운 캐스팅

as?, as!를 사용해서 자식 클래스의 인스턴스로 사용할 수 있도록 컴파일러에게 타입 정보를 전환해줌

조건부 다운 캐스팅

  • as! 사용 하여 캐스팅에 실패할 경우 nil을 반환함
  • 그렇기 때문에 optional type
var optionalCasted: Student?

optionalCasted = mike as? UniversityStudent
optionalCasted = jenny as? UniversityStudent // nil
optionalCasted = jina as? UniversityStudent // nil
optionalCasted = jina as? Student // nil

강제 다운 캐스팅

  • as! 사용
  • 캐스팅에 실패하면, 즉 원하는 캐스팅 타입에 부합하지 않으면 런타임 오류 발생
  • 캐스팅에 성공하면 옵셔널이 아닌 일반 타입 반환
var forcedCasted: Student

forcedCasted = mike as! UniversityStudent
//forcedCasted = jenny as! UniversityStudent // 런타임 오류
//forcedCasted = jina as! UniversityStudent // 런타임 오류
//forcedCasted = jina as! Student // 런타임 오류

활용

func doSomethingWithSwitch(someone: Person) {
    switch someone {
    case is UniversityStudent:
        (someone as! UniversityStudent).goToMT()
    case is Student:
        (someone as! Student).goToSchool()
    case is Person:
        (someone as! Person).breath()
    }
}

doSomethingWithSwitch(someone: mike as Person) // 멤버쉽 트레이닝을 갑니다 신남!
doSomethingWithSwitch(someone: mike) // 멤버쉽 트레이닝을 갑니다 신남!
doSomethingWithSwitch(someone: jenny) // 등교를 합니다
doSomethingWithSwitch(someone: wansik) // 숨을 쉽니다
func doSomething(someone: Person) {
    if let universityStudent = someone as? UniversityStudent {
        universityStudent.goToMT()
    } else if let student = someone as? Student {
        student.goToSchool()
    } else if let person = someone as? Person {
        person.breath()
    }
}

doSomething(someone: mike as Person) // 멤버쉽 트레이닝을 갑니다 신남!
doSomething(someone: mike) // 멤버쉽 트레이닝을 갑니다 신남!
doSomething(someone: jenny) // 등교를 합니다
doSomething(someone: wansik) // 숨을 쉽니다

아직 어느 상황에서 사용하게 될지는 모르겠다.

as, as!, as?

  • as
    • 업캐스팅, switch 문에서만 사용가능!!!!!1
    • 컴파일 타임에 실행됨
  • as?, as!
    • 런타임에 실행!

Reference