🔥 Any와 AnyObject를 위한 타입 캐스팅

531자
6분

Swift는 모든 타입의 값을 담을 수 있는 타입으로 작업하기 위한 두 가지 특별한 타입을 제공해요:

  • Any는 함수 타입을 포함한 모든 타입의 인스턴스를 나타낼 수 있어요.
  • AnyObject는 모든 클래스 타입의 인스턴스를 나타낼 수 있지요.

AnyAnyObject는 그것들이 제공하는 동작과 기능이 명시적으로 필요할 때만 사용하세요. 코드에서 작업할 것으로 예상되는 타입에 대해 구체적으로 명시하는 것이 항상 더 좋답니다.

다음은 함수 타입과 비클래스 타입을 포함한 다양한 타입의 혼합으로 작업하기 위해 Any를 사용하는 예시예요. 이 예제는 Any 타입의 값을 저장할 수 있는 things라는 배열을 생성해요:

var things: [Any] = []
 
things.append(0)            // Int 값 추가
things.append(0.0)          // Double 값 추가
things.append(42)           // 또 다른 Int 값 추가
things.append(3.14159)      // 또 다른 Double 값 추가
things.append("hello")      // String 값 추가
things.append((3.0, 5.0))   // (Double, Double) 타입의 튜플 추가
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))  // Movie 인스턴스 추가
things.append({ (name: String) -> String in "Hello, \(name)" })        // String을 받아 String을 반환하는 클로저 추가
swift

things 배열은 두 개의 Int 값, 두 개의 Double 값, 하나의 String 값, (Double, Double) 타입의 튜플, "Ghostbusters" 영화, 그리고 String 값을 받아 다른 String 값을 반환하는 클로저 표현식을 포함하고 있어요.

Any 또는 AnyObject 타입으로만 알려진 상수나 변수의 특정 타입을 발견하기 위해, switch 문의 case에서 is 또는 as 패턴을 사용할 수 있어요. 아래 예제는 things 배열의 항목을 반복하고 switch 문으로 각 항목의 타입을 쿼리합니다. switch 문의 여러 case는 매치된 값을 지정된 타입의 상수에 바인딩하여 그 값을 출력할 수 있게 해줘요:

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 movie as Movie:
        print("a movie called \(movie.name), dir. \(movie.director)")
    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
swift

이 예제는 things 배열의 각 항목을 순회하면서, switch 문을 사용해 각 항목의 타입을 확인해요. 각 case는 특정 타입과 매치되는지 검사하고, 매치되는 경우 해당 값을 적절한 타입의 상수에 바인딩하지요. 그리고 바인딩된 값을 사용해 적절한 메시지를 출력합니다.

이처럼 AnyAnyObject를 사용하면 다양한 타입을 하나의 컬렉션에 저장하고 다룰 수 있어요. 하지만 이는 타입 안전성을 희생시키는 것이기 때문에, 꼭 필요한 경우에만 사용해야 해요. 가능하면 항상 구체적인 타입을 사용하는 것이 좋답니다.

아래는 Any 타입의 값을 다룰 때 주의해야 할 점들을 정리한 도표예요:

lecture image

이 도표는 Any 타입을 사용할 때 명시적인 타입 캐스팅이 필요하다는 것을 보여줘요. as 연산자를 사용해 구체적인 타입으로 캐스팅을 시도할 수 있는데, 이때 캐스팅이 성공하면 해당 타입의 값으로 사용할 수 있지만 실패하면 런타임 에러가 발생해요. 또한 is 연산자를 사용해 특정 타입인지 확인할 수 있어요. 특정 타입이라면 그 타입에 맞는 처리를 할 수 있지만, 그렇지 않다면 다른 타입에 대한 처리가 별도로 필요하지요.