Jerry's Bakery

[iOS] Type Casting(타입 캐스팅) 본문

개발/iOS

[iOS] Type Casting(타입 캐스팅)

_Jerry 2021. 10. 28. 16:20

안녕하세요 Jerry입니다.

 

TypeCasting(타입 캐스팅)에 대해 알아보겠습니다.

 

iOS를 공부하면서 정리하는 것이기 때문에 미흡한 점이 있을 수 있습니다. 부족한 점에 대해 댓글 남겨주시면 감사하겠습니다.

 

 

이 글은 아래 문서를 기반으로 만들어졌습니다.

 

Type Casting — The Swift Programming Language (Swift 5.5)

Type Casting 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 tw

docs.swift.org

 

타입 캐스팅이란?

타입캐스팅은 아래 두 가지 방법을 말합니다.

  1. 인스턴스의 타입을 확인하는 방법(is 연산자 사용)
  2. 인스턴스의 슈퍼클래스나 서브클래스로 변환할 수 있는 방법(as 연산자 사용)

 

두가지 방법을 예를 들어 확인해보겠습니다.

 

인스턴스 타입 확인(is 연산자 사용)

타입 캐스팅을 확인하기 위해 아래와 같이 클래스를 선언하였습니다.

class Food {
    let name: String

    init(name: String) {
        self.name = name
    }
}

class Chinesefood: Food {
    let type: String

    init(name: String, type: String) {
        self.type = type
        super.init(name: name)
    }
}

class KoreanFood: Food {
    let type: String

    init(name: String, type: String) {
        self.type = type
        super.init(name: name)
    }
}

그리고, 인스턴스 배열을 아래와 같이 선언합니다.

let instanceList = [ Chinesefood(name: "cake", type: "케이크"),
                     KoreanFood(name: "김치", type: "전통음식"),
                     Chinesefood(name: "smoothie", type: "Drink"),
                     KoreanFood(name: "불고기", type: "전통음식"),
                     Chinesefood(name: "coke", type: "Drink"),
                     KoreanFood(name: "치킨", type: "현대음식")]

instanceList 배열을 for문으로 탐색하면서 인스턴스가 어떤 타입인지 확인하고, 어떤 타입인지 출력합니다. 아래 코드에서 is로 인스턴스가 특정 클래스의 타입인지 확인하는 것을 알 수 있습니다.

for instance in instanceList {
    if instance is KoreanFood {
        print("한국음식")
    } else if instance is Chinesefood {
        print("중국음식")
    }
}

그러나 아래코드와 같이 인스턴스가 Food 클래스인지 확인하는 코드를 추가하여 실행하면 전부다 "음식"이 출력되는 것을 알 수 있습니다.

for instance in instanceList {
    if instance is Food {
        print("음식")
    } else if instance is KoreanFood {
        print("한국음식")
    } else if instance is Chinesefood {
        print("중국음식")
    }
}

왜 그럴까요?

instanceList 타입을 살펴보면 KoreanFood, ChineseFood 클래스가 아닌 Food 클래스 배열임을 확인할 수 있습니다.

Swift에서 변수 또는 상수의 타입을 선언해주지 않으면, 타입을 정할 때 타입추론을 이용하여 어떤 타입으로 선언할지 결정합니다. 현재 instanceList배열에는 KoreanFood, ChineseFood 클래스의 인스턴스가 포함되어 있고, 두 클래스가 같지 않다는 것을 확인했고, 최대한 공통적인 부분을 찾으려고 했습니다. Swift는 "슈퍼클래스가 같다" 라는것을 알게 되었고 Food 클래스 타입의 배열로 업캐스팅 하였습니다.

 

만약 KoreanFood, ChineseFood 클래스의 인스턴스 외의 새로운 인스턴스가 들어가게 된다면 어떻게 될까요?

아래 코드처럼 변경하니 "이기종(서로 다른) 클래스 간의 인스턴스는 [Any]로만 유추할 수 있다"는 오류가 발생하게 됩니다. 이는 Swift가 타입 추론에 실패하였기 때문에 오류를 발생시킨 것이고, 의도적인 부분이라면 [Any] 어노테이션을 삽입하면 됩니다.

class Book {
    let name: String
    
    init(name: String) {
        self.name = name
    }
}

let instanceList = [ Chinesefood(name: "cake", type: "케이크"),
                     KoreanFood(name: "김치", type: "전통음식"),
                     Chinesefood(name: "smoothie", type: "Drink"),
                     KoreanFood(name: "불고기", type: "전통음식"),
                     Chinesefood(name: "coke", type: "Drink"),
                     KoreanFood(name: "치킨", type: "현대음식")
                     Book(name: "책")]

 

인스턴스의 슈퍼클래스나, 서브클래스로 변환(as 연산자 사용)

위 코드에서 instance의 모든 변수를 출력하는 코드를 작성해보겠습니다.

아래 코드를 실행하면 오류가 발생하는 것을 알 수 있습니다.

for instance in instanceList {
    if instance is KoreanFood {
        print(instance.name, instance.type)
    } else if instance is Chinesefood {
        print(instance.name, instance.type)
    }
}

위에서 보셨던 것처럼, instanceList배열의 타입은 [Food]로 타입 추론을 통해 업캐스팅 되어있습니다. Food 클래스의 변수인 name에만 접근할 수 있습니다. type을 사용할 수 있도록 하려면 인스턴스의 서브클래스로 다운캐스팅을 해주어야 합니다. 이 과정을 as연산자로 수행할 것입니다.

 

as 뒤에는 느낌표(!)와 물음표(?) 두 가지를 사용할 수 있습니다. 느낌표의 경우 반드시 타입 변환이 가능하다고 생각될 때 타입 캐스팅을 수행할 수 있고, 물음표를 사용하면 옵셔널 바인딩을 사용하여 타입캐스팅을 수행할 수 있습니다.

 

위의 오류를 as연산을 사용하여 해결해보겠습니다.

 

우선, 느낌표를 이용하여 해결해보겠습니다. 느낌표는 반드시 타입변환이 가능하다고 생각될 때 할 수 있는 것이므로, 유의하여 사용해야 합니다.

for instance in instanceList {
    if instance is KoreanFood {
        let koreanFoodInstance = instance as! KoreanFood
        print(koreanFoodInstance.name, koreanFoodInstance.type)
    } else if instance is Chinesefood {
        let chineseFoodInstance = instance as! Chinesefood
        print(chineseFoodInstance.name, chineseFoodInstance.type)
    }
}

다음은, 물음표 연산을 사용하여 타입캐스팅을 수행해보겠습니다.

위의 코드를 그대로 물음표로 변환하면 오류를 발생시킵니다. 변수의 값의 타입을 확인해보니 옵셔널 타입임을 알 수 있습니다.

위와 같은 오류를 해결하려면 인스턴스의 변수에 접근할 때 옵셔널체이닝을 사용하여 해제하거나, 옵셔널 바인딩을 통해 해결해야 합니다.

아래 그림은 옵셔널 바인딩을 통하여 타입 캐스팅을 수행한 것입니다.

for instance in instanceList {
    if instance is KoreanFood {
        if let koreanFoodInstance = instance as? KoreanFood {
            print(koreanFoodInstance.name, koreanFoodInstance.type)
        }
    } else if instance is Chinesefood {
        if let chineseFoodInstance = instance as? Chinesefood {
            print(chineseFoodInstance.name, chineseFoodInstance.type)
        }
    }
}

저의 경우, 처음 커스텀 테이블 뷰를 사용해서 뷰컨트롤러에서 불러왔을 때 커스텀 테이블뷰의 변수를 불러올 수 없다는 오류가 발생되었습니다.

 

 

Comments