티스토리 뷰

https://modelinspring.tistory.com/61

 

[Swift/iOS] 타입캐스팅(1) - 타입 변환, 타입캐스팅, 데이터 타입 확인, 메타 타입, 메타 타입 타입

스위프트는 데이터 타입 안전을 위해서 서로 다른 타입끼리의 값 교환을 제한함 1. 기존 언어의 타입 변환과 스위프트 타입 변환 실패할 수 있는 이니셜라이져 // 실패 할 수 있는 이니셜라이져 v

modelinspring.tistory.com

에 이어서 작성되었습니다

 

스위프트는

  • 데이터 타입 안전을 위해서 서로 다른 타입끼리의 값 교환을 제한함

1. 기존 언어의 타입 변환과 스위프트 타입 변환

  • 실패할 수 있는 이니셜라이져
// 실패 할 수 있는 이니셜라이져
var string:Value String = "000111222"
var integerValue: Int? = Int(stringValue)

print(integerValue) // Optional(111222)

stringValue = "AAA000111"
integerValue = Int(stringValue)

print(integerValue) // nil

2. 스위프트 타입캐스팅

  • 스위프트의 타입캐스팅은 인스턴스의 타입을 확인하거나 자신을 다른 타입의 인스턴스인양 행세할 수 있는 방법
  • is와 as로 타입을 확인하거나 다른 타입으로 전환(cast)할 수 있음
  • 프로토콜 준수 여부 확인 가능
class Car {
    let name: String
    let door: Int
    
    var description: String {
        return "\\(name) is \\(String(door))door car"
    }
    
    init(door:Int) {
        self.name = "Car"
        self.door = door
    }
}

class Z4: Car {
    var price: Int
    
    override var description: String {
        return "\\(name) has \\(String(door)) door and price is \\(String(price))만원"
    }
    
    init(door: Int, price: Int) {
        self.price = price
        super.init(door: door)
    }
}

class EV6: Car {
    var speed: String
    
    override var description: String {
        return "\\(name) has \\(String(door)) door and \\(speed) speed"
    }
    
    init(door: Int, speed: String) {
        self.speed  = speed
        super.init(door: door)
    }
}
  • Z4 와 EV6는 Car 클래스를 상속
  • Car의 프로퍼티를 Z4와 EV6가 가지고 있음
  • Car는 Z4나 EV6인 척 할 수 없지만, Z4와 EV6는 Car인 척 할 수 있음 → Z4와 EV6는 Car의 모든 특성을 가지고 있기 때문

3. 데이터 타입 확인

  • is를 사용하여 인스턴스가 어떤 클래스의 인스턴스인지 타입을 확인할 수 있음
  • 인스턴스가 해당 클래스의 인스턴스거나 그 자식클래스의 인스턴스면 true, 그렇지 않으면 false 반환
let car: Car = Car(door: 5)
print(car.description) // Car is 5 door car

let myZ4: Z4 = Z4(door: 2, price: 8000)
print(myZ4.description) // Car has 2 door and price is 8000만원

let yourEV6 : EV6 = EV6(door: 5, speed: "fast")
print(yourEV6.description) // Car has 5 door and fast speed

print(car is Car) // true
print(car is Z4) // false
print(car is EV6) //false

print(myZ4 is Car) // true
print(yourEV6 is Car) // true

print(myZ4 is EV6) // false
print(yourEV6 is EV6) // true
  • car는 Z4나 EV6가 될 수 없지만, myZ4나 yourEV6는 Car가 될 수 있음
  • myZ4와 yourEV6는 서로 다른 타입

메타 타입(Meta Type) 타입

  • 타입의 타입
  • 클래스, 구조체, 열거형 타입 등 타입의 타입
  • ‘타입 그 자체'가 하나의 타입으로 또 표현될 수 있음
  • 타입의 이름 뒤에 .Tpye을 붙이면 메타 타입
  • 프로토콜 타입의 메타 타입은 .Protocol
  • .self를 사용하면 타입을 값으로 표현할 수 있음
  • SomeClass.self 표현하면 SomeClass의 인스턴스가 아닌 SomeClass를 값으로 표현한 값을 반환
  • SomProtocol.self이라고 표현하면 SomeProtocol을 준수하는 타입의 인스턴스가 아닌 SomeProtocol 프로토콜을 값으로 표현한 값 반환
protocol SomeProtocol { }
class SomeClass: SomeProtocol { }

let intType: Int.Type = Int.self
let stringType: String.Type = String.self
let classType: SomeClass.Type = SomeClass.self
let protocolProtocol: SomeProtocol.Protocol = SomeProtocol.self

var someType: Any.Type

someType = intType
print(someType) // Int

someType = stringType
print(someType) // String

someType = classType
print(someType) // SomeClass

someType = protocolProtocol // SomeProtocol
print(someType)
  • 인스턴스 self와 타입 self
    • .self 표현은 값 뒤에 써주면 그 값 자신을 반환
    • 타입 이름 뒤에 써주면 타입을 표현하는 값을 반환
    • “stringValue”.self 는 “stringValue”를, String.self는 String 타입 그 자체
    print(type(of:car) == Car.self)// true
    print(type(of:car) == Z4.self) // false
    print(type(of:car) == EV6.self) // false
    
    print(type(of:car) == Z4.self) // false
    print(type(of:myZ4) == Z4.self) // true
    print(type(of:yourEV6) == Z4.self) // false
    
    print(type(of:car) == EV6.self) // false
    print(type(of:myZ4) == EV6.self) // false
    print(type(of:yourEV6) == EV6.self) // true
    

4. 다운캐스팅

  • 어떤 클래스 타입의 변수나 상수가 해당 클래스의 인스턴스가 아닌 다른 클래스의 인스턴스인 것 처럼 사용될 수 있음
//EV6 타입의 인스턴스를 참조하는 Car 타입의 actingConstant
let actionConstant: Car = EV6(door: 5, speed: "fast")
print(actionConstant.description) // Car has 5 door and fast speed
  • actingConstant는 실제로는 EV6타입이지만, Car타입인 것처럼 사용될 수 있도록 선언되었음
  • 본래의 인스턴스인 EV6로 접근해야할 때는 EV6타입으로 타입변환을 해주어야 함
    • == 다운캐스팅
    • 부모클래스를 자식클래스타입으로 캐스팅하는 것(down)
    • Car 클래스를 자식 클래스인 EV6 타입으로 캐스팅 하는 것
    • 클래스의 인스턴스에만 사용하는 것은 아니고, Any 타입에서 다르 타입으로 캐스팅 할때도 이용함

타입캐스트 연산자 as! 와 as?

  • 타입캐스트 연산자를 사용하여 자식클래스 타입으로 사용할 수 있음
  • 다운캐스팅은 실패할 수 있으므로 as? 연산자는 다운캐스팅이 실패했을 때 nil을 반환함
  • as! 연산자는 다운캐스팅에 실패할 경우 런타임 오류 발생
  • as?는 반환타입이 옵셔널이고, as!는 아님
  • 다운캐스팅이 실패할 가능성이 있다면 조건부 연산자 as?를 사용해야 함
    • 성공할 경우, 옵셔널 타입의 인스턴스를 반환
    • 실패할 경우, nil 반환
  • 다운캐스팅이 무조건 성공하는 상황이면, 강제연산자인 as!를 사용할 수 있음
    • 다운캐스팅이 성공할 경우, 옵셔널이 아닌 인스턴스를 반환
    • 실패할 경우, 런타임 오류 발생….
//다운캐스팅
if let actionOne: Z4 = car as? Z4 {
    print("This car is Z4")
} else {
    print(car.description)
} // Car is 5door car

if let actionTwo: EV6 = car as? EV6 {
    print("This car is EV6")
} else {
    print(car.description)
} // Car is 5door car

if let actionThree: Car = car as? Car {
    print("This car is Just Car")
} else {
    print(car.description)
} // This car is Just Car

if let actionFour: Z4 = z4 as? Z4 {
    print("This car is Z4")
} else {
    print(car.description)
} // This car is Z4

if let actionFive: EV6 = z4 as? EV6 {
    print("This car is EV6")
} else {
    print(car.description)
} // Car is 5door car

if let actionSix: Car = car as? Car {
    print("This car is Just Car")
} else {
    print(car.description)
} // This car is Just Car

let castedCar: Car = z4 as! Car // Success

let castedZ4: Z4 = car as! Z4 // Runtime Error
  • 항상 성공하는 다운캐스팅
let castedCar: Car = ev6 as Car // 컴파일러도 항상 성공한다는 것을 알고 있음

5. Any, AnyObject의 타입캐스팅

  • Any와 AnyObject는 특정 타입을 지정하지 않고 여러 타입의 값을 할당할 수 있음
    • Any는 모든 함수 타입을 포함한 모든 타입
    • AnyObject는 클래스 타입만 뜻함
  • Any와 AnyObject는 오류발생확률이 높으니 사용을 지양
  • 어떤 타입의 인스턴스인지 체크해보기
func checkType(of item:AnyObject) {
    if item is EV6 {
        print("Item is EV6")
    } else if item is Z4 {
        print("Item is Z4")
    } else if item is Car {
        print("Item is Car")
    } else {
        print("Unknown Type")
    }
}

checkType(of: car) // Item is Car
checkType(of: z4) // Item is Z4
checkType(of: ev6) // Item is EV6
checkType(of: actionConstant) // EV6
  • 어떤 타입인지 체크하고 사용할 수 있도록 캐스팅
print("checkType")
func checkType(of item:AnyObject) {
    if item is EV6 {
        print("Item is EV6")
    } else if item is Z4 {
        print("Item is Z4")
    } else if item is Car {
        print("Item is Car")
    } else {
        print("Unknown Type")
    }
}

checkType(of: car) // Item is Car
checkType(of: z4) // Item is Z4
checkType(of: ev6) // Item is EV6
checkType(of: actionConstant) // EV6

func castTypeToAppropriate(item: AnyObject) {
    if let castedItem: EV6 = item as? EV6 {
        print(castedItem.description)
    } else if let castedItem: Z4 = item as? Z4 {
        print(castedItem.description)
    } else if let castedItem: Car = item as? Car {
        print(castedItem.description)
    } else {
        print("Unknown Type")
    }
}

castTypeToAppropriate(item: car) // Car is 5door car
castTypeToAppropriate(item: z4) // Car has 2 door and price is 8000만원
castTypeToAppropriate(item: ev6) // Car has 5 door and fast speed
castTypeToAppropriate(item: actionConstant) // Car has 5 door anvd fast speed
  • AnyObject와 달리 모든 클래스의 인스턴스를 취할 수 있는 Any
func checkAnyType(of item: Any) {
    switch item {
    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 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 ev6 as EV6:
        print(ev6.description)
    case let stringConverter as (String) -> String:
        print(stringConverter("soul"))
    default:
        print("something else : \\(type(of: item))")
    }
}

checkAnyType(of: 0) // zero as an Int
checkAnyType(of: 0.0) // zero as a Double
checkAnyType(of: 42) // an Integer value of 42
checkAnyType(of: 3.1415) // a positive value of 3.1415
checkAnyType(of: -0.25) // some other double value that I don't want to print
checkAnyType(of: "Hello") // a string value of "Hello"
checkAnyType(of: (3.0, 5.0)) // an (x, y) point at 3.0, 5.0
checkAnyType(of: ev6) // Car has 5 door and fast speed
checkAnyType(of: car) // something else : Car
checkAnyType(of: {(name: String) -> String in "Hello, \\(name)"}) // Hello, soul
  • 다양한 타입의 인스턴스를 전달인자로 호출했음
  • 그as, let 값 바인딩 등을 사용하여 타입을 확인하고 동작을 확인
댓글