[iOS] Type Casting

Sangwon Shin·2022년 1월 14일
0

iOS

목록 보기
9/9

📦 Type Casting

타입 캐스팅은 지금까지 앱을 구현하면서 많이 사용했었는데 정확하게 짚고 넘어가려고 합니다. (블로그에서도 아주 간단하게 정리했었네요.)


🏗 Swift 타입 변환

스위프트에서의 타입 캐스팅은 다른 언어에서의 타입 캐스팅과는 의미가 다릅니다.

double value = 3.82;
int convertedValue = (int)value; // convertedValue = 3
convertedValue = 4.1; // convertedValue = 4

먼저 C언어에서의 타입 캐스팅을 확인 해보겠습니다.
C언어에서는 데이터 타입 변환을 통해서 double 타입의 변수를 int 타입으로 변환 할 수 있습니다.

var value: Double = 3.82
var convertedValue: Int = Int(value)
convertedValue = 4.1 // Error

Swift 에서도 유사한 형태로 Double -> Int 형태로 데이터 타입의 형태를 변경할 수 있습니다.

하지만 Int 구조체의 이니셜라이저를 통해서 새로운 Int 인스턴스를 생성하는 것이지 데이터 타입 변환으로 볼 수 없습니다.

그럼 Swift에서 타입캐스팅은 무엇을 의미할까요?

Swift의 타입캐스팅은 인스턴스의 타입을 확인하거나 자신을 다른 타입의 인스턴스처럼 사용하는 것을 의미합니다. (as, is)

예제를 통해서 살펴보겠습니다.

class Coffee {
    let name: String
    let shot: Int
    
    init(shot: Int) {
    	self.shot = shot
        self.name = "coffee"
     }
}

class Latte: Coffee {
    var flavor: String
    
    init(flavor: String, shot: Int) {
    	self.flavor = flavor
        super.init(shot:shot)
    }
}

class Americano: Coffee {
    let iced: Bool
    
    init(shot: Int, iced: Bool) {
    	self.iced = iced
        super.init(shot: shot)
    }
}

위와 같이 Coffee 라는 클래스를 상속받는 Latte,Americano 라는 두 클래스가 있습니다.

클래스를 상속받으면 부모 클래스의 고유한 프로퍼티들을 하위 클래스가 가지게 되므로 Coffee 클래스가 갖는 특성들을 Latte,Americano클래스도 모두 포함하고 있음을 알 수 있습니다.

다시 말하면 Latte,AmericanoCoffee인 척할 수 있습니다.
반대의 경우는 불가능합니다. Latte,Americano 의 고유한 프로퍼티를 Coffee는 가지고 있지 않기 때문입니다.

이런 개념을 가지고 Swift 타입 캐스팅을 의미하는 1) 타입확인 2) 다른 타입의 인스턴스 처럼 사용 에 대해서 살펴 보겠습니다.


🔎 데이터 타입 확인

타입 연산자 is를 사용해서 인스턴스가 어떤 클래스(어떤 클래스의 자식 클래스) 의 인스턴스 타입인지 확인할 수 있습니다.

let coffee: Coffee = Coffee(shot: 1)
let myCoffee: Americano = Americano(shot: 2, iced: false)
let yourCoffee: Latte = Latte(flavor: "good", shot: 2)

coffee is Coffee // true
coffee is Americano // false
coffee is Latte // false

myCoffee is Coffee // true
yourCoffee is Coffee // true

myCoffee is Latte // false
yourCoffee is Latte // true

위의 예제를 통해서 알 수 있듯이 인스턴스가 해당 클래스의 인스턴스 또는 해당 클래스의 자식클래스의 인스턴스라면 true 를 반환합니다.

타입 연산자 is 이외에도 Meta Type을 통해서 타입을 확인할 수 있습니다.

🤷‍♂️ 메타타입

메타타입은 타입의 타입을 의미합니다.

타입의 타입이라니 이게 무슨 뜻일까요?? 별로 느낌이 와닿지 않지만 우리는 메타타입을 사용한 적이 있습니다!

tableView.register(BoardsTableViewCell.self, forCellReuseIdentifier: BoardsTableViewCell.identifier)

위와 같이 BoardsTableViewCell.selfBoardsTableViewCell의 타입 자체의 변수 형태입니다.

간단한 예제를 통해서 개념을 공부해보겠습니다.

import Foundation

class Person {
    
    static let nations = "korea"
    let name: String
    
    func printMajor(major: String) {
        print("major is \(major)")
    }
    
    init(name: String) {
        self.name = name
    }
}

let sangwon: Person = Person(name: "sangwon")
let meta: Person.Type = type(of: sangwon)
let personType: Person.Type = Person.self

//print(sangwon.nations)
print(meta)
print(meta.nations)

print(personType)
print(personType.nations)

Person() 은 인스턴스, Person은 인스턴스를 나타내는 type 입니다.

그래서 Person() 은 인스턴스 sangwon에서는 타입 프로퍼티 nations 에 접근할 수 없습니다.

우리는 print(Person.nations) 와 같은 형태로 타입 이름을 이용해서 타입 프로퍼티를 사용해왔습니다.

하지만 위와같이 Some.Type 형태의 클래스 자체의 타입을 의미하는 메타타입을 사용해서 타입 프로퍼티에 접근할 수 있습니다.

Person.Type = Person 의 메타타입
Person.self  = Person 메타타입의 값

앞선 예제를 메타타입을 통해서 타입을 확인 해보겠습니다.

type(of: coffee) == Coffee.self // true
type(of: coffee) == Americano.self // false
type(of: coffee) == Latte.self // false

type(of: yourCoffee) == Coffee.self // false
type(of: myCoffee) == Coffee.self // false

type(of: myCoffee) == Latte.self // false
type(of: yourCoffee) == Latte.self // true

is 와 달리 같은 타입일 때만 true 가 출력되는 것을 확인 할 수 있습니다.


👎 다운캐스팅

다운캐스팅은 크게 두 가지로 나눌 수 있습니다.

  • 부모클래스의 타입을 자식클래스의 타입으로 캐스팅
  • Any 타입에서 다른 타입으로 캐스팅

첫번째 경우를 먼저 살펴보겠습니다.

// 다운캐스팅 실패하는 경우
if let actingOne: Americano = coffee as? Americano {
    print("This is Americano")
} else {
    print("Fail")
}

// 부모클래스로의 업캐스팅 (항상 성공)
if let actingTwo: Coffee = myCoffee as? Coffee {
    print("This is Coffee")
} else {
    print("Fail")
}

//Latte 인스턴스를 Coffee 로 업캐스팅
let castedCoffee: Coffee = yourCoffee as! Coffee
//업캐스팅된 castedCoffee를 다시 Latte 로 다운캐스팅
var recastedCoffee: Latte = castedCoffee as! Latte

as 를 통한 타입캐스트는 반환타입이 옵셔널인 as?반환타입이 옵셔널이 아닌 as! 두 가지가 존재합니다.

따라서, 상황에 맞게 옵셔널 바인딩을 해주면 됩니다!

Any, AnyObject 타입캐스팅

Swift에는 특정타입을 지정하지 않고 여러 타입의 값을 할당할 수 있는 Any, AnyObject 라는 타입이 있습니다.

  • Any: 함수 타입을 포함한 모든 타입에 대한 인스턴스(구조체, 열거형)
  • AnyObject: 클래스 타입의 인스턴스

Any, AnyObject 모두 어떤 타입으로 된 멤버를 가지고 있는지 컴파일 시점에서는 알 수 없고, 런타임 시점에 결정되기 때문에 타입 캐스팅을 하지 않으면 멤버에 대한 접근도 불가능합니다.

Any 를 이용해서 타입캐스팅을 하는 경우를 확인 해보겠습니다.

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({ (name: String) -> String in "Hello, \(name)" })

for thing in things {
    switch thing {
    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 double 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 stringConverter as (String) -> String:
        print(stringConverter("Michael"))
    default:
        print("something else")
    }
}
// zero as an Int
// zero as a Double
// an integer value of 42
// a positive double value of 3.14159
// a string value of "hello"
// an (x, y) point at 3.0, 5.0
// a movie called Ghostbusters, dir. Ivan Reitman
// Hello, Michael

위와 같이 Any 를 사용하는 경우 모든 타입의 인스턴스를 멤버로 가질 수 있고, 타입 캐스팅을 통해서 값을 확인 할 수 있습니다.


🏷 P.S.

새해가 밝으면 열심히 블로그도 쓰고 출시한 앱 업데이트도 주기적으로 하려고 했는데 게을러서 그런지 쉽지가 않습니다 ㅠㅠ

이제 수업시간에 배운 내용 중 블로그에 정리하지 않은 내용들은 한번씩 학습하고 벽을 느겼던 클로저, 동기/비동기, ARC 가 있습니다.

그래도 프로젝트 기간에 하나씩 천천히 이해해가면서 블로그에 정리하도록 하겠습니다!

profile
개발자가 되고싶어요

0개의 댓글