* 내가 읽으려고 내 맘대로 번역한 글.
* 원문 : https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html

 

 

 

 

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 two operators provide a simple and expressive way to check the type of a value or cast a value to a different type.

swift에서 타입 캐스팅은 is, as 연산자로 구현되어 있다.

이 두개의 연산자는 값의 타입을 검사하고 값을 다른 타입으로 바꾸는 간단하고 표현적인 방법을 제공한다.

 

You can also use type casting to check whether a type conforms to a protocol, as described in Checking for Protocol Conformance.

또한 타입 캐스팅을 이용해서 프로토콜을 준수하는지 검사할수 있다.

 

Defining a Class Hierarchy for Type Casting

You can use type casting with a hierarchy of classes and subclasses to check the type of a particular class instance and to cast that instance to another class within the same hierarchy. The three code snippets below define a hierarchy of classes and an array containing instances of those classes, for use in an example of type casting.

클래스와 서브클래스의 계층으로 타입 캐스팅을 사용하여 특정 클래스 인스턴스의 타입을 검사하고

인스턴스를 같은 계층안에 있는 다른 클래스로 변환할수 있다.

아래 3개의 코드 조각은 클래스의 계층도를 정의하고 배열은 이 클래스들의 인스턴스들을 포함하여 타입 캐스팅의 예제에 사용한다.

 

The first snippet defines a new base class called MediaItem. This class provides basic functionality for any kind of item that appears in a digital media library. Specifically, it declares a name property of type String, and an init name initializer. (It is assumed that all media items, including all movies and songs, will have a name.)

처음 코드조각은 MediaItem 이라는 새로운 기본 클래스를 정의한다.

이 클래스는 디지털 미디어 라이브러리에 보여지는 모든 아이템에 기본적인 기능을 제공한다.

특히 문자열 타입의 name 속성을 선언하고, 초기화에서 이름을 초기화한다.

(모든 영화, 노래등 모든 미디어 아이템은 이름을 가진다고 가정한다)

class MediaItem {
	var name: String
	init(name: String) {
		self.name = name
	}
}

 

The next snippet defines two subclasses of MediaItem. The first subclass, Movie, encapsulates additional information about a movie or film. It adds a director property on top of the base MediaItem class, with a corresponding initializer. The second subclass, Song, adds an artist property and initializer on top of the base class:

두번째 코드조각은 MediaItem의 서브클래스 2개를 정의한다.

첫번째 서브클래스 Movie 는 영화또는 필름에 대한 추가 정보를 캡슐화한다.

기본 MediaItem 클래스의 위에 director 속성을 추가하고, 관련된 초기화를 했다.

두번째 서브클래스 Song 은 기본 클래스 위에 artist 속성과 초기화를 추가하였다.

class Movie: MediaItem {
	var director: String
	init(name: String, director: String) {
		self.director = director
		super.init(name: name)
	}
}

class Song: MediaItem {
	var artist: String
	init(name: String, artist: String) {
		self.artist = artist
		super.init(name: name)
	}
}

 

The final snippet creates a constant array called library, which contains two Movie instances and three Song instances. The type of the library array is inferred by initializing it with the contents of an array literal. Swift’s type checker is able to deduce that Movie and Song have a common superclass of MediaItem, and so it infers a type of [MediaItem] for the library array:

마지막 코드조각은 library라는 상수배열을 생성하고, 2개의 Movie 인스턴스와 3개의 Song 인스턴스를 넣었다.

library 배열의 타입은 배열 리터널의 내용을 기반으로 초기화에 의해 추정된다.

swift의 타입 검사는 Movie와 Song이 공통된 슈퍼클래스 MdiaItem을 가진다는걸 추론할수 있어서,

library 배열을 [MediaItem] 타입으로 추정한다.

let library = [
	Movie(name: "Casablanca", director: "Michael Curtiz"),
	Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
	Movie(name: "Citizen Kane", director: "Orson Welles"),
	Song(name: "The One And Only", artist: "Chesney Hawkes"),
	Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
// the type of "library" is inferred to be [MediaItem]

 

The items stored in library are still Movie and Song instances behind the scenes. However, if you iterate over the contents of this array, the items you receive back are typed as MediaItem, and not as Movie or Song. In order to work with them as their native type, you need to check their type, or downcast them to a different type, as described below.

library에 저장된 아이템들은 뒷단에서 여전히 Movie 와 Song 의 인스턴스이다.

하지만 너가 이 배열의 내용들을 열거하면, 너가 받는 아이템들의 타입은 MediaItem 이고, Movie 또는 Song 이 아니다.

그것들을 진짜 타입으로 작업하기 위해서는, 타입을 검사해야만 하고, 아래에서 기술하듯이 다른 타입으로 downcast 해야 한다.

 

Checking Type

Use the type check operator (is) to check whether an instance is of a certain subclass type. The type check operator returns true if the instance is of that subclass type and false if it is not.

타입 검사 연산자 (is)를 사용하여 인스턴스가 특정 서브클래스 타입인지 여부를 검사한다.

타입 검사 연산자는 인스턴스가 그 서브클래스 타입이면 true를 리턴하고 아니면 false 를 리턴한다.

 

The example below defines two variables, movieCount and songCount, which count the number of Movie and Song instances in the library array:

아래 예제는 movieCount, songCount 2개의 변수를 선언하였고, library 배열안에 있는 Movie와 Song 인스턴스의 갯수를 센다.

var movieCount = 0
var songCount = 0

for item in library {
	if item is Movie {
		movieCount += 1
	} else if item is Song {
		songCount += 1
	}
}

print("Media library contains \(movieCount) movies and \(songCount) songs")
// Prints "Media library contains 2 movies and 3 songs"

 

This example iterates through all items in the library array. On each pass, the for-in loop sets the item constant to the next MediaItem in the array.

이 예제는 library 배열이 모든 아이템들을 열거하였다.

각 단계에서 for-in 루프는 아이템 상수를 배열안에 있는 다음 MediaItem 으로 설정한다.

 

item is Movie returns true if the current MediaItem is a Movie instance and false if it is not. Similarly, item is Song checks whether the item is a Song instance. At the end of the for-in loop, the values of movieCount and songCount contain a count of how many MediaItem instances were found of each type.

if item is Movie 문장은 현재 MediaItem 이 Movie 인스턴스면 true 를 리턴하고 아니면 false를 리턴한다.

비슷하게 if item is Song 문장은 아이템이 Song 인스턴스인지 여부를 검사한다.

for-in 루프 마지막에서, MediaItem 인스턴스가 각 타입으로 얼마나 많이 발견되었는지의 숫자를

movieCount 와 songCount 의 값은 가지게된다.

 

Downcasting

A constant or variable of a certain class type may actually refer to an instance of a subclass behind the scenes. Where you believe this is the case, you can try to downcast to the subclass type with a type cast operator (as? or as!).

특정 클래스 타입의 상수와 변수는 뒷단에서 실제로 인스턴스의 서브클래스로 참조될수 있다.

너가 이런 경우라고 믿는다면, 서브클래스 타입으로 downcast 하려고 타입 캐스트 연산자 (as? as!)를 사용하여 시도할수 있다.

 

Because downcasting can fail, the type cast operator comes in two different forms. The conditional form, as?, returns an optional value of the type you are trying to downcast to. The forced form, as!, attempts the downcast and force-unwraps the result as a single compound action.

downcasting이 실패할수 있으니까, 타입 캐스트 연산자는 2가지 다른 형태가 있다.

조건부형태 as? 는 너가 downcast 하려는 타입의 옵셔널 값을 리턴한다.

강제형태 as! 는 downcast를 시도하고 결과를 강제로 꺼낸다.

 

Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.

downcast가 성공하리라는 확신이 없을때 조건부형태 타입 캐스트 연산자 as? 를 사용해라.

이 형태의 연산자는 항상 옵셔널 값을 리턴하고, downcast 가 불가능할때는 nil 값이다.

이것은 downcast 의 성공여부를 확인할수 있게 해준다.

 

Use the forced form of the type cast operator (as!) only when you are sure that the downcast will always succeed. This form of the operator will trigger a runtime error if you try to downcast to an incorrect class type.

downcast가 항상 성공할거는 확신이 있으면 강제형태 타입 캐스트 연산자 as! 를 사용해라.

이 형태의 연산자는 잘못된 클래스 타입으로 downcast 하려고 시도하면 런타임 에러를 발생시킨다.

 

The example below iterates over each MediaItem in library, and prints an appropriate description for each item. To do this, it needs to access each item as a true Movie or Song, and not just as a MediaItem. This is necessary in order for it to be able to access the director or artist property of a Movie or Song for use in the description.

아래 예제는 library 안에 있는 각 MediaItem 을 열거하고, 각 아이템에 대하여 적절한 설명을 출력한다.

그러기 위해서는, 각 아이템에 접근해서 그 아이템이 MediaItem 타입으로서가 아니고 Movie 또는 Song 타입인지 알아야 한다.

설명에 사용할 Movie 또는 Song 의 director 또는 artist 속성에 접근해야 할 필요성이 있다.

 

In this example, each item in the array might be a Movie, or it might be a Song. You don’t know in advance which actual class to use for each item, and so it is appropriate to use the conditional form of the type cast operator (as?) to check the downcast each time through the loop:

이 예제에서, 배열의 각 아이템은 Movie 또는 Song 일수 있다.

각 항목에 실제로 어떤 클래스를 사용해야 하는지 미리 알지 못하니까,

루프를 돌면서 타입 캐스트 연산자의 조건부 형태 as? 를 사용해서 downcast 를 검사하는게 적절하다.

for item in library {
	if let movie = item as? Movie {
		print("Movie: \(movie.name), dir. \(movie.director)")
	} else if let song = item as? Song {
		print("Song: \(song.name), by \(song.artist)")
	}
}

// Movie: Casablanca, dir. Michael Curtiz
// Song: Blue Suede Shoes, by Elvis Presley
// Movie: Citizen Kane, dir. Orson Welles
// Song: The One And Only, by Chesney Hawkes
// Song: Never Gonna Give You Up, by Rick Astley

 

The example starts by trying to downcast the current item as a Movie. Because item is a MediaItem instance, it’s possible that it might be a Movie; equally, it’s also possible that it might be a Song, or even just a base MediaItem. Because of this uncertainty, the as? form of the type cast operator returns an optional value when attempting to downcast to a subclass type. The result of item as? Movie is of type Movie?, or “optional Movie”.

이 예제는 먼저 현재 아이템을 Movie로 downcast 하려고 시도한다.

왜냐면 아이템은 MediaItem 인스턴스이고, Movie 일수도 있으니까. 동일하게 역시 Song 일수도 있고,

또는 단지 기본 MediaItem 일수도 있다.

이게 확실하지 않으니까  타입 캐스트 연산자 as? 는 서브클래스 타입으로 downcast 를 시도할때 옵셔널 값을 리턴한다.

item as? Movie 의 결과는 Movie? 또는 옵셔널 Movie 이다.

 

Downcasting to Movie fails when applied to the Song instances in the library array. To cope with this, the example above uses optional binding to check whether the optional Movie actually contains a value (that is, to find out whether the downcast succeeded.) This optional binding is written “if let movie = item as? Movie”, which can be read as:

library 배열안에 있는 Song 인스턴스에 Movie로 downcasting 하려는 시도는 실패한다.

이걸 대처하기 위해서, 위의 예제는 옵셔널 Movie가 실제로 값을 가지고 있는지 검사하기 위해서 옵셔널 바인딩을 사용했다.

(downcast가 성공했는지 여부를 알기 위해서)

옵셔널 바인딩은 "if let movie = item as? Movie" 로 쓰여졌고, 이것은 아래를 뜻한다.

 

“Try to access item as a Movie. If this is successful, set a new temporary constant called movie to the value stored in the returned optional Movie.”

"아이템을 Movie 로써 접근 시도해라. 성공하면 movie 라는 새로운 임시 상수에 리턴되는 옵셔널 Movie 값을 저장해라"

 

If the downcasting succeeds, the properties of movie are then used to print a description for that Movie instance, including the name of its director. A similar principle is used to check for Song instances, and to print an appropriate description (including artist name) whenever a Song is found in the library.

downcasting 이 성공하면, 감독의 이름을 포함한 영화의 속성들이 Movie 인스턴스에 대한 설명을 출력하는데 사용된다.

비슷한 원칙이 Song 인스턴스에 대한 검사에 사용되고,

library에서 Song이 발견될때마다, 아티스트 이름을 포함한 적절한 설명이 출력되는데 사용된다.

 

NOTE

Casting does not actually modify the instance or change its values. The underlying instance remains the same; it is simply treated and accessed as an instance of the type to which it has been cast.

캐스팅은 실제로 인스턴스를 수정하거나 값을 변경하지 않는다.

그 인스턴스는 동일하게 남는다. 이것은 단지 캐스트 된 타입의 인스턴스로 접근하고 취급될 뿐이다.

 

Type Casting for Any and AnyObject

Swift provides two special types for working with nonspecific types:

swift 는 타입이 지정되지 않은 두가지 특별한 타입을 제공한다.

  • Any can represent an instance of any type at all, including function types.
  • AnyObject can represent an instance of any class type.
  • Any 는 함수 타입을 포함하여 모든 타입의 인스턴스로 표현될수 있다.
  • AnyObject 는 모든 클래스 타입의 인스턴스로 표현될수 있다.

 

Use Any and AnyObject only when you explicitly need the behavior and capabilities they provide. It is always better to be specific about the types you expect to work with in your code.

Any, AnyObject 는 제공되는 동작과 기능이 확실히 필요할때는 사용해라.

너의 코드에서 예상할수 있는 타입에 대해서 기술하는것이 항상 더 좋다.

 

Here’s an example of using Any to work with a mix of different types, including function types and nonclass types. The example creates an array called things, which can store values of type Any:

함수 타입과 클래스타입이 아닌것을 포함하여, 여러개가 섞여있는 다양한 타입을 다루려고 할때 Any 를 사용하는 예제가 있다.

이 예제는 things 라는 배열을 생성하였고, 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(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })

 

The things array contains two Int values, two Double values, a String value, a tuple of type (Double, Double), the movie “Ghostbusters”, and a closure expression that takes a String value and returns another String value.

things 배열은 두개의 Int 값, 두개의 Double 값, 한개의 String 값, 한개의 (Double, Double) 튜플,

"Ghostbusters" 영화, 문자열을 받고 다른 문자열을 리턴하는 클로져 표현식을 포함한다.

 

To discover the specific type of a constant or variable that is known only to be of type Any or AnyObject, you can use an is or as pattern in a switch statement’s cases. The example below iterates over the items in the things array and queries the type of each item with a switch statement. Several of the switch statement’s cases bind their matched value to a constant of the specified type to enable its value to be printed:

Any 또는 AnyObject 로만 알려진 변수나 상수의 실제 타입을 알아내기 위해서,

switch 구문의 case 에 is 또는 as 패턴을 사용할수 있다.

아래 예제는 things 배열안에 있는 모든 아이템들을 나열하고 어떤 타입인지 조회한다.

여러개의 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

 

NOTE

The Any type represents values of any type, including optional types. Swift gives you a warning if you use an optional value where a value of type Any is expected. If you really do need to use an optional value as an Any value, you can use the as operator to explicitly cast the optional to Any, as shown below.

Any 타입은 모든 타입의 값을 표현할수 있다, 옵셔널 타입도 포함해서.

Any 타입이 있어야 할 곳에 옵셔널 값을 사용하려고 하면 swift 는 경고를 준다.

만약 너가 진짜로 Any 값으로 옵셔널 값이 필요하다면,

as 연산자를 사용해서 명시적으로 옵셔널을 Any로 캐스트 할수 있다. 아래에 보이는 것처럼.

let optionalNumber: Int? = 3
things.append(optionalNumber) // Warning
things.append(optionalNumber as Any) // No warning

 

* 이석우 추가.

바로 위의 switch 예제에서 보면 downcast 하는데 그냥 as 를 사용했다 (as? as! 가 아니고)

switch문에서는 as? as! 를 사용할수 없고 as 만 사용해야 한단다 (왜왜왜???)

그리고 그냥 as 는 upcast 에도 사용된다고 한다.

반응형

'iOS 초보' 카테고리의 다른 글

[swift5.1번역] 20.Extensions  (0) 2019.12.04
[swift5.1번역] 19.Nested Types  (0) 2019.11.26
[swift5.1번역] 17.Error Handling  (0) 2019.11.14
[swift5.1번역] 16.Optional Chaining  (0) 2019.10.11
[swift5.1번역] 15.Deinitialization  (0) 2019.09.17
Posted by 돌비
,