* 내가 읽으려고 내 맘대로 번역한 글.
* 원문 : https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
* 초기화는 objective-c 와 많이 다르다. 주의해서 읽어라.
Initialization
Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.
초기화란 클래스, 구조체, 열거형의 인스턴스를 사용하기 위해 준비하는 처리이다.
이 처리는 인스턴스의 각 저장 속성에 초기값을 설정하고,
새로운 인스턴스가 사용되기 전에 다른 설정을 수행하고, 초기화하는것과 연관되어 있다.
You implement this initialization process by defining initializers, which are like special methods that can be called to create a new instance of a particular type. Unlike Objective-C initializers, Swift initializers do not return a value. Their primary role is to ensure that new instances of a type are correctly initialized before they are used for the first time.
특정 타입의 새로운 인스턴스를 만들기위해 호출될수 있는 특별한 메소드와 같은 초기화를 정의해서 이런 초기화 처리를 구현한다.
objective-c 초기화와 달리, swift 초기화는 값을 리턴하지 않는다.
초기화의 가장 중요한 목표는 새로운 인스턴스가 처음으로 사용되기 전에 올바르게 초기화되도록 하는것이다.
Instances of class types can also implement a deinitializer, which performs any custom cleanup just before an instance of that class is deallocated. For more information about deinitializers, see Deinitialization.
클래스 타입의 인스턴스는 deinitializer 또한 구현할수 있이서, 인스턴스가 해제되기 전에 커스텀 정리를 수행할수 있다.
Setting Initial Values for Stored Properties
Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.
클래스와 구조체는 인스턴스가 생성되는 시점에 모든 저장 속성이 적절한 초기값으로 설정되어야만 한다.
저장 속성은 불확실한 상태로 둘수 없다.
You can set an initial value for a stored property within an initializer, or by assigning a default property value as part of the property’s definition. These actions are described in the following sections.
초기화할때 저장 속성에 초기 값을 설정하거나, 속성 정의의 일부로써 기본 속성 값을 할당할수 있다.
이 동작은 이어진 섹션에서 설명한다.
NOTE
When you assign a default value to a stored property, or set its initial value within an initializer, the value of that property is set directly, without calling any property observers.
저장 속성에 기본 값을 할당하거나, 초기화에서 초기값을 설정하거나,
속성 옵저버는 호출되지 않고 직접 설정된다.
Initializers
Initializers are called to create a new instance of a particular type. In its simplest form, an initializer is like an instance method with no parameters, written using the init keyword:
특정 타입의 새로운 인스턴스가 생성될때 초기화가 호출된다.
가장 간단한 형태의 초기화는 인스턴스 매소드 처럼 생겼고, 매개변수가 없고, init 키워드를 사용한다.
init() {
// perform some initialization here
}
The example below defines a new structure called Fahrenheit to store temperatures expressed in the Fahrenheit scale. The Fahrenheit structure has one stored property, temperature, which is of type Double:
아래 예제는 Fahrenheit라는 새로운 구조체를 정의해서 화씨 단위로 표현된 온도를 저장한다.
Fahrenheit 구조체는 하나의 Double 타입 저장 속성 temperature를 가진다.
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// Prints "The default temperature is 32.0° Fahrenheit"
The structure defines a single initializer, init, with no parameters, which initializes the stored temperature with a value of 32.0 (the freezing point of water in degrees Fahrenheit).
이 구조체는 하나의 초기화를 정의한다. init, 매개변수 없고, temperature 값을 32.0 으로 저장.
(화씨 단위에서 물이 어는 온도)
Default Property Values
You can set the initial value of a stored property from within an initializer, as shown above. Alternatively, specify a default property value as part of the property’s declaration. You specify a default property value by assigning an initial value to the property when it is defined.
위에서 본대로 초기화 안에서 저장 속성의 초기 값을 설정할수 있고,
속성 정의에 기본값을 기술할수도 있다.
속성이 정의될때 초기 값을 설정하여 속성의 기본값을 기술할수 있다.
NOTE
If a property always takes the same initial value, provide a default value rather than setting a value within an initializer. The end result is the same, but the default value ties the property’s initialization more closely to its declaration. It makes for shorter, clearer initializers and enables you to infer the type of the property from its default value. The default value also makes it easier for you to take advantage of default initializers and initializer inheritance, as described later in this chapter.
속성이 항상 같은 초기값을 가진다면 초기화에서 값을 설정하기 보다는 기본값을 넣어라.
결과는 같지만 기본값은 정의에 더 가깝게 붙어있다 (선언에 기본값을 같이 넣으면 보기 쉽다는 뜻)
이것은 더 짧고, 초기화를 깔끔하게 만들어 주고, 기본값으로 부터 속성의 타입을 추론할수 있게 한다.
기본값은 또한 기본 초기화와 초기화 상속을 더 쉽게 해주는 이점이 있다. 이 챕터의 뒤에서 설명한다.
You can write the Fahrenheit structure from above in a simpler form by providing a default value for its temperature property at the point that the property is declared:
struct Fahrenheit {
var temperature = 32.0
}
Customizing Initialization
You can customize the initialization process with input parameters and optional property types, or by assigning constant properties during initialization, as described in the following sections.
초기화 처리를 매개변수와 옵셔널 속성 타입을 가지고 커스텀 할수 있거나,
상수 속성을 초기화 동안 할당할수 있다. 이어지는 섹션에서 기술한다.
Initialization Parameters
You can provide initialization parameters as part of an initializer’s definition, to define the types and names of values that customize the initialization process. Initialization parameters have the same capabilities and syntax as function and method parameters.
초기화의 정의에 매개변수를 넣을수 있어서, 초기화 진행에 필요한 타입과 이름을 정의할수 있다.
초기화 매개변수는 함수와 메소드의 매개변수와 동일한 능력과 문법을 가진다.
The following example defines a structure called Celsius, which stores temperatures expressed in degrees Celsius. The Celsius structure implements two custom initializers called init(fromFahrenheit:) and init(fromKelvin:), which initialize a new instance of the structure with a value from a different temperature scale:
다음 예제는 Celsius 라는 구조체를 정의하고, 섭씨 등급의 온도 표현을 저장한다.
Celsius 구조체는 init(fromFahrenheit:) 와 init(fromKelvin:) 2개의 커스텀 초기화를 구현하여서,
다른 온도 단위로부터 오는 값으로 새로운 인스턴스를 초기화한다.
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0
The first initializer has a single initialization parameter with an argument label of fromFahrenheit and a parameter name of fahrenheit. The second initializer has a single initialization parameter with an argument label of fromKelvin and a parameter name of kelvin. Both initializers convert their single argument into the corresponding Celsius value and store this value in a property called temperatureInCelsius.
첫번째 초기화는 하나의 매개변수를 가지며 매개변수 label 은 fromFahrenheit 고 매개변수 이름은 fahrenheit 다.
두번째 초기화는 하나의 매개변수를 가지며 매개변수 label 은 fromKelvin 이고 매개변수 이름은 kelvin 이다.
두 초기화는 매개변수를 Celsius 값으로 변환하고 temperatureInCelsius 속성에 저장한다.
Parameter Names and Argument Labels
As with function and method parameters, initialization parameters can have both a parameter name for use within the initializer’s body and an argument label for use when calling the initializer.
함수와 메소드 매개변수와 마찬가지로, 초기화 매개변수는 매개변수 이름과 (초기화 body안에서 사용되는)
매개변수 label (초기화를 호출할때 사용되는)을 가진다.
However, initializers do not have an identifying function name before their parentheses in the way that functions and methods do. Therefore, the names and types of an initializer’s parameters play a particularly important role in identifying which initializer should be called. Because of this, Swift provides an automatic argument label for every parameter in an initializer if you don’t provide one.
그렇지만, 함수나 메소드와 다르게 초기화는 괄호앞에 구별할수 있는 함수 이름을 가지지 않는다 (함수 이름이 항상 init).
그러므로, 초기화 매개변수의 이름과 타입은 어떤 초기화가 호출되어야 하는지 구별하는데 특히 중요하다.
이런 이유로, 너가 label을 넣지 않았다면, swift 는 자동으로 초기화의 모든 매개변수에 label을 제공한다.
The following example defines a structure called Color, with three constant properties called red, green, and blue. These properties store a value between 0.0 and 1.0 to indicate the amount of red, green, and blue in the color.
다음의 예제는 Color라는 구조체를 정의하고, red, green, blue 3개의 상수 속성을 가진다.
이 속성들은 0.0 ~ 1.0의 값을 저장하고, 색의 red, green, blue 양을 가리킨다.
Color provides an initializer with three appropriately named parameters of type Double for its red, green, and blue components. Color also provides a second initializer with a single white parameter, which is used to provide the same value for all three color components.
Color는 red, green, blue 요소에 대해 Double 타입의 이름이 있는 매개변수를 초기화에 제공한다.
Color는 또한 두번째 초기화로써 white 매개변수 하나를 가지는 초기화도 제공하고, 이는 3개의 색 요소에 동일한 값을 저장하는데 쓰인다.
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
Both initializers can be used to create a new Color instance, by providing named values for each initializer parameter:
두개의 초기화는 새로운 Color 인스턴스를 만드는데 사용할수 있고, 각 매개변수마다 이름이 붙은 값을 제공해야 한다.
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
Note that it is not possible to call these initializers without using argument labels. Argument labels must always be used in an initializer if they are defined, and omitting them is a compile-time error:
매개변수 label 없이 초기화를 호출할수 없다는걸 주목해라.
매개변수 label은 정의되어 있다면 항상 사용되어야만 한다. 생략하면 컴파일 에러 난다.
let veryGreen = Color(0.0, 1.0, 0.0)
// this reports a compile-time error - argument labels are required
Initializer Parameters Without Argument Labels
If you do not want to use an argument label for an initializer parameter, write an underscore (_) instead of an explicit argument label for that parameter to override the default behavior.
초기화 매개변수에 label을 사용하기 원하지 않으면, 명시적인 매개변수 label 대신에 underscore (_) 를 사용해서 기본 동작을 대체해라.
Here’s an expanded version of the Celsius example from Initialization Parameters above, with an additional initializer to create a new Celsius instance from a Double value that is already in the Celsius scale:
여기 Celsius 예제의 확장된 버젼이 있다.
추가된 초기화는 Celsius 등급의 Double 값으로 부터 새로운 Celsius 인스턴스를 만든다.
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0
The initializer call Celsius(37.0) is clear in its intent without the need for an argument label. It is therefore appropriate to write this initializer as init(_ celsius: Double) so that it can be called by providing an unnamed Double value.
Celsius(37.0) 초기화 호출은 매개변수 label 없이도 의도가 명확하다.
그러므로 적절한 init(_ celsius: Double) 을 작성해서 이름없는 Double 값으로 호출이 가능하도록 하였다.
Optional Property Types
If your custom type has a stored property that is logically allowed to have “no value”—perhaps because its value cannot be set during initialization, or because it is allowed to have “no value” at some later point—declare the property with an optional type. Properties of optional type are automatically initialized with a value of nil, indicating that the property is deliberately intended to have “no value yet” during initialization.
저장 속성이 논리적으로 "no value" 일수 있으면 그 속성을 optional 타입으로 정의해라.
-아마 초기화 동안에 값을 설정할수 없거나, 또는 특정 시점에 "no value" 가 허용되기 때문에-
optional 타입의 속성은 자동으로 nil 값으로 초기화 되고, 초기화 과정동안 "아직 값이 없음" 이 되도록 신중히 의도되었음을 나타낸다.
The following example defines a class called SurveyQuestion, with an optional String property called response:
다음 예제는 SurveyQuestion 이라는 클래스는 정의하고, response라는 optional String 속성을 가진다.
class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.ask()
// Prints "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."
The response to a survey question cannot be known until it is asked, and so the response property is declared with a type of String?, or “optional String”. It is automatically assigned a default value of nil, meaning “no string yet”, when a new instance of SurveyQuestion is initialized.
투표 질문의 응답은 물어보기 전까지는 모른다, 그래서 response 속성은 String? 또는 optional String 타입으로 정의되었다.
SurveyQuestion의 새로운 인스턴스가 초기화 되엇을때, 이것은 자동으로 기본값 nil 로 할당되고, 그 뜻은 "아직 문자열 없음" 이다.
Assigning Constant Properties During Initialization
You can assign a value to a constant property at any point during initialization, as long as it is set to a definite value by the time initialization finishes. Once a constant property is assigned a value, it can’t be further modified.
초기화가 끝났을때 명확할 값으로 설정되기만 하면, 초기화 과정중에서는 언제든지 상수 속성에 값을 할당할수 있다.
한번 상수 속성에 값이 할당되면, 나중에 바꿀수 없다.
NOTE
For class instances, a constant property can be modified during initialization only by the class that introduces it. It cannot be modified by a subclass.
클래스 인스턴스에서, 상수 속성은 그 클래스에 의해서만 초기화 과정중에 수정할수 있다.
서브클래스에서는 수정할수 없다.
You can revise the SurveyQuestion example from above to use a constant property rather than a variable property for the text property of the question, to indicate that the question does not change once an instance of SurveyQuestion is created. Even though the text property is now a constant, it can still be set within the class’s initializer:
위의 SurveyQuestion 예제를 수정하여 text 속성을 변수 속성 대신 상수 속성을 사용하여,
SurveyQuestion 인스턴스가 일단 생성되면 질문을 바꿀수 없다는걸 나타낼수 있다.
text 속성이 현재 상수 일지라도, 클래스의 초기화 안에서는 여전히 설정될수 있다.
class SurveyQuestion {
let text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// Prints "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"
Default Initializers
Swift provides a default initializer for any structure or class that provides default values for all of its properties and does not provide at least one initializer itself. The default initializer simply creates a new instance with all of its properties set to their default values.
초기화를 하나도 가지지 않는 모든 구조체나 클래스에 swift는 기본 초기화를 제공하여 모든 속성에 기본값을 넣는다.
기본 초기화는 단순히 새로운 인스턴스를 만들고 모든 속성에 기본 값을 설정한다.
This example defines a class called ShoppingListItem, which encapsulates the name, quantity, and purchase state of an item in a shopping list:
이 예제는 ShoppingListItem이라는 클래스를 정의하여, 쇼핑 리스트안 아이템의 이름, 수량, 구매상태를 캡슐화한다.
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
Because all properties of the ShoppingListItem class have default values, and because it is a base class with no superclass, ShoppingListItem automatically gains a default initializer implementation that creates a new instance with all of its properties set to their default values. (The name property is an optional String property, and so it automatically receives a default value of nil, even though this value is not written in the code.) The example above uses the default initializer for the ShoppingListItem class to create a new instance of the class with initializer syntax, written as ShoppingListItem(), and assigns this new instance to a variable called item.
ShoppingListItem의 모든 속성이 기본값을 가지기 때문에, 슈퍼클래스가 없는 기본 클래스이기 때문에,
ShoppingListItem은 새로운 인스턴스를 만들고 모든 속성을 기본값으로 설정하는 기본 초기화 구현을 자동으로 가진다.
(name 속성은 optional String 이라서, 코드에서 기술하지 않았지만 기본값이 nil 이다)
위의 예제는 ShoppingListItem에 대한 기본 초기화를 사용해서,
ShoppingListItem() 초기화 문법으로 새로운 인스턴스를 만들고,
item 변수에 새로운 인스턴스를 할당한다.
Memberwise Initializers for Structure Types
Structure types automatically receive a memberwise initializer if they don’t define any of their own custom initializers. Unlike a default initializer, the structure receives a memberwise initializer even if it has stored properties that don’t have default values.
구조체는 커스텀 초기화를 하나도 정의하지 않았으면, 자동으로 memberwise 초기화를 받는다.
기본 초기화와 다르게, 구조체는 기본값이 없는 저장 속성을 가진다고 해도 memberwise 초기화를 받는다.
The memberwise initializer is a shorthand way to initialize the member properties of new structure instances. Initial values for the properties of the new instance can be passed to the memberwise initializer by name.
memberwise 초기화는 새로운 구조체 인스턴스의 멤버 속성을 초기화하는 간단한 방법이다.
새로운 인스턴스의 속성에 대한 초기값은 이름으로 memberwise 초기화에 전달될수 있다.
The example below defines a structure called Size with two properties called width and height. Both properties are inferred to be of type Double by assigning a default value of 0.0.
아래 예제는 Size라는 구조체를 정의하고, width, height라는 2개의 속성을 가지고 있다.
기본값 0.0 이기 때문에 둘다 Double 타입으로 추정된다.
The Size structure automatically receives an init(width:height:) memberwise initializer, which you can use to initialize a new Size instance:
Size구조체는 자동으로 init(width:height:) memberwise 초기화를 받아서, 너가 새로운 Size 인스턴스를 초기화하는데 사용가능하다.
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
When you call a memberwise initializer, you can omit values for any properties that have default values. In the example above, the Size structure has a default value for both its height and width properties. You can omit either property or both properties, and the initializer uses the default value for anything you omit—for example:
memberwise 초기화를 호출할때, 기본값을 가지는 속성은 생략 가능하다.
위의 예에서, Size 구조체는 height, width 속성 둘다 기본값을 가지고 있다.
너는 속성중 하나 또는 둘다 생략할수 있고, 초기화는 너가 생략한 속성에 기본값을 사용한다.
let zeroByTwo = Size(height: 2.0)
print(zeroByTwo.width, zeroByTwo.height)
// Prints "0.0 2.0"
let zeroByZero = Size()
print(zeroByZero.width, zeroByZero.height)
// Prints "0.0 0.0"
Initializer Delegation for Value Types
Initializers can call other initializers to perform part of an instance’s initialization. This process, known as initializer delegation, avoids duplicating code across multiple initializers.
초기화는 인스턴스를 초기화할때 다른 초기화를 호출할수 있다.
초기화 위임이라고 알려진 이 처리는, 여러개의 초기화에 중복되는 코드를 피하게 해준다.
The rules for how initializer delegation works, and for what forms of delegation are allowed, are different for value types and class types. Value types (structures and enumerations) do not support inheritance, and so their initializer delegation process is relatively simple, because they can only delegate to another initializer that they provide themselves. Classes, however, can inherit from other classes, as described in Inheritance. This means that classes have additional responsibilities for ensuring that all stored properties they inherit are assigned a suitable value during initialization. These responsibilities are described in Class Inheritance and Initialization below.
규칙은 (초기화 위임이 어떻게 동작하는지, 어떤 위임 형태가 허용되는지) 값타입과 클래스타입이 다르다.
값타입 (구조체, 열거형)은 상속을 지원하지 않아서, 초기화 위임 처리는 상대적으로 간단하다.
왜냐면 스스로 제공하는 다른 초기화에 의해서만 위임될수 있기 때문이다.
클래스는 다른 클래스로부터 상속될수 있다. 이것은 초기화 동안에 상속받은 모든 저장 속성에 알맞은 값을 설정해야 하는
추가적인 책임이 있다는 걸 뜻한다.
이 책임은 아래에서 설명한다.
For value types, you use self.init to refer to other initializers from the same value type when writing your own custom initializers. You can call self.init only from within an initializer.
값타입에서, 커스텀 초기화를 작성할때 같은 값타입에서 self.init 으로 다른 초기화를 참조할수 있다.
self.init 은 초기화 안에서만 호출할수 있다.
Note that if you define a custom initializer for a value type, you will no longer have access to the default initializer (or the memberwise initializer, if it is a structure) for that type. This constraint prevents a situation in which additional essential setup provided in a more complex initializer is accidentally circumvented by someone using one of the automatic initializers.
값타입에서 커스텀 초기화를 정의하면, 기본 초기화 (구조체일때는 memberwise 초기화) 에 더이상 접근할수 없다.
이 제약은 기본 초기화를 사용하는 사람이 복잡한 초기화안에서 필수적으로 설정되어야 하는 것을 우회하는 상황을 막아준다.
이석우 추가 : 초기화할때 복잡한 설정이 꼭 필요한데, 어떤 사람이 기본 초기화를 호출해서, 설정이 안되는 상황을 막아준다는 뜻인거 같다.
NOTE
If you want your custom value type to be initializable with the default initializer and memberwise initializer, and also with your own custom initializers, write your custom initializers in an extension rather than as part of the value type’s original implementation. For more information, see Extensions.
값타입에 기본 초기화, memberwise 초기화, 커스텀 초기화를 모두 원하면,
커스텀 초기화를 타입의 원래 구현에 넣지 말고 확장에 넣어라.
확장에 대한 정보는 몇 챕터 뒤에 나온다.
The following example defines a custom Rect structure to represent a geometric rectangle. The example requires two supporting structures called Size and Point, both of which provide default values of 0.0 for all of their properties:
다음 예제는 Rect 구조체를 정의하여 기하학적인 사각형을 표현한다.
이 예제는 Size, Point 라는 구조체의 지원이 필요하다.
둘다 모든 속성의 기본값을 0.0 으로 제공한다.
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
You can initialize the Rect structure below in one of three ways—by using its default zero-initialized origin and size property values, by providing a specific origin point and size, or by providing a specific center point and size. These initialization options are represented by three custom initializers that are part of the Rect structure’s definition:
Rect 구조체를 아래의 3개 방법중 하나로 초기화 할수 있다.
- origin, size 속성값을 기본값 0으로 사용하는 초기화
- 특정 point와 size를 제공하는 초기화
- 특정 center와 size를 제공하는 초기화.
이러한 초기화들은 Rect 구조체 정의의 일부로써 3개의 커스텀 초기화에 의해 표현된다.
struct Rect {
var origin = Point()
var size = Size()
init() {}
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
The first Rect initializer, init(), is functionally the same as the default initializer that the structure would have received if it did not have its own custom initializers. This initializer has an empty body, represented by an empty pair of curly braces {}. Calling this initializer returns a Rect instance whose origin and size properties are both initialized with the default values of Point(x: 0.0, y: 0.0) and Size(width: 0.0, height: 0.0) from their property definitions:
첫번째 Rect 초기화, init() 은 커스텀 초기화가 없을때 구조체가 기본적으로 제공하는 기본 초기화와 기능적으로 동일하다.
이 초기화는 빈 body를 가졌고, 빈 {} 쌍으로 표현된다.
이 초기화를 호출하면 Rect 인스턴스를 리턴하고,
origin과 size 속성은 각각 기본값 Point(x:0.0, y:0.0) 과 Size(width:0.0, height:0.0) 으로 초기화되었다.
let basicRect = Rect()
// basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)
The second Rect initializer, init(origin:size:), is functionally the same as the memberwise initializer that the structure would have received if it did not have its own custom initializers. This initializer simply assigns the origin and size argument values to the appropriate stored properties:
두번째 Rect 초기화, init(origina:size:) 은 커스텀 초기화가 없을때 구조체가 기본적으로 제공하는 memberwise 초기화와 기능적으로 동일하다. 이 초기화는 단순히 origin과 size 매개변수 값을 적절한 저장 속성에 할당한다.
let originRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
// originRect's origin is (2.0, 2.0) and its size is (5.0, 5.0)
The third Rect initializer, init(center:size:), is slightly more complex. It starts by calculating an appropriate origin point based on a center point and a size value. It then calls (or delegates) to the init(origin:size:) initializer, which stores the new origin and size values in the appropriate properties:
세번째 Rect 초기화, init(center:size:) 는 조금 복잡하다.
center 위치와 size 값을 기반으로 적절한 origin 위치를 계산한다.
그러고 나서 init(origin:size) 초기화를 호출(위임) 하여, 새로운 origin과 size 값을 적절한 속성에 저장한다.
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))
// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
The init(center:size:) initializer could have assigned the new values of origin and size to the appropriate properties itself. However, it is more convenient (and clearer in intent) for the init(center:size:) initializer to take advantage of an existing initializer that already provides exactly that functionality.
init(center:size:) 초기화는 새로운 origin과 size 값을 적절한 속성에 스스로 할당할수도 있다.
하지만 정확히 그 기능을 하고 이미 존재하는 초기화를 init(center:size) 초기화가 사용하는것이 더 편리하고 의도를 명확하게 한다.
NOTE
For an alternative way to write this example without defining the init() and init(origin:size:) initializers yourself, see Extensions.
이 예제를 init() 과 init(origin:size:) 초기화 없이 작성하는 다른 방법은 확장에서 볼수 있다.
Class Inheritance and Initialization
All of a class’s stored properties—including any properties the class inherits from its superclass—must be assigned an initial value during initialization.
클래스의 모든 저장 속성 - 슈퍼클래스에서 상속된 모든 속성을 포함하여 - 은 초기화 동안 초기 값이 할당 되어야만 한다.
Swift defines two kinds of initializers for class types to help ensure all stored properties receive an initial value. These are known as designated initializers and convenience initializers.
swift 는 클래스 타입에 대해 2가지 종류의 초기화를 정의하여, 모든 저장 속성이 초기값을 받도록 한다.
이를 designated 초기화와 convenience 초기화라고 한다.
Designated Initializers and Convenience Initializers
Designated initializers are the primary initializers for a class. A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.
designated 초기화는 클래스에 대해 기본 초기화다.
designated 초기화는 클래스에 있는 모든 속성들을 완전히 초기화하고,
적절한 슈퍼클래스의 초기화를 호출하여 초기화 처리를 계속 할수 있다.
Classes tend to have very few designated initializers, and it is quite common for a class to have only one. Designated initializers are “funnel” points through which initialization takes place, and through which the initialization process continues up the superclass chain.
클래스는 매우 적은 수의 designated 초기화를 가지는 경향이 있고, 오직 하나만 가지는 클래스가 매우 일반적이다.
designated 초기화는 초기화가 위치하고, 슈퍼클래스 체인에서 초기화 처리가 계속되는 지점이다.
이석우 추가 : designated 초기화는 자신의 초기화를 처리하고, 슈퍼클래스 단위에서 초기화 처리가 계속되는 연결점이다.
이게 자연스러운 번역 같음.
Every class must have at least one designated initializer. In some cases, this requirement is satisfied by inheriting one or more designated initializers from a superclass, as described in Automatic Initializer Inheritance below.
모든 클래스는 적어도 하나의 designated 초기화를 가져야만 한다.
가끔 이 요구조건은 슈퍼클래스로부터 designated 초기화를 상속받아 만족될수 있다.
Convenience initializers are secondary, supporting initializers for a class. You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer’s parameters set to default values. You can also define a convenience initializer to create an instance of that class for a specific use case or input value type.
convenience 초기화는 클래스의 초기화를 지원하는 보조이다.
convenience 초기화를 정의하여 같은 클래스의 designated 초기화를 호출하고, designated 초기화의 매개변수를 기본 값으로 설정할수 있다.
또한 convenience 초기화를 정의하여 특별한 경우나 특별한 입력 값에 대하여 클래스의 인스턴스를 만들수 있다.
You do not have to provide convenience initializers if your class does not require them. Create convenience initializers whenever a shortcut to a common initialization pattern will save time or make initialization of the class clearer in intent.
너의 클래스가 convenience 초기화를 필요로 하지 않으면 만들 필요가 없다.
일반적인 초기화 패턴에 대한 지름길이 시간을 절약하거나, 클래스의 초기화 의도를 명확하게 할때
convenience 초기화를 만들어라.
Syntax for Designated and Convenience Initializers
Designated initializers for classes are written in the same way as simple initializers for value types:
클래스에 대한 designated 초기화는 값타입에 대한 단순한 초기화처럼 단순하게 쓴다.
init(parameters) {
statements
}
Convenience initializers are written in the same style, but with the convenience modifier placed before the init keyword, separated by a space:
convenience 초기화도 같은 스타일로 작성하지만, init 키워드 앞에 한칸띄고 convenience 키워드를 적는다.
convenience init(parameters) {
statements
}
Initializer Delegation for Class Types
To simplify the relationships between designated and convenience initializers, Swift applies the following three rules for delegation calls between initializers:
designated 와 convenience 초기화 사이의 관계를 간략히 하기 위해서,
swift 는 다음의 3가지 규칙을 초기화들 사이의 위임 호출 에 적용한다.
Rule 1
A designated initializer must call a designated initializer from its immediate superclass.
Rule 2
A convenience initializer must call another initializer from the same class.
Rule 3
A convenience initializer must ultimately call a designated initializer.
Rule 1
designated 초기화는 반드시 바로 위 슈퍼클래스에 있는 designated 초기화를 호출해야 한다.
Rule 2
convenience 초기화는 반드시 같은 클래스에 있는 다른 초기화를 호출해야 한다.
Rule 3
convenience 초기화는 반드시 designated 초기화를 마지막에 호출해야 한다.
A simple way to remember this is:
기억하기 쉬운 방법은
- Designated initializers must always delegate up.
- Convenience initializers must always delegate across.
- designated 초기화는 반드시 항상 위로 위임한다.
- convenience 초기화는 반드시 항상 옆으로 위임한다.
These rules are illustrated in the figure below:
이 규칙은 아래 그림에 설명되어 있다.
Here, the superclass has a single designated initializer and two convenience initializers. One convenience initializer calls another convenience initializer, which in turn calls the single designated initializer. This satisfies rules 2 and 3 from above. The superclass does not itself have a further superclass, and so rule 1 does not apply.
여기 슈퍼클래스는 designated 초기화 하나와 convenience 초기화 두개를 가지고 있다.
convenience 초기화는 다른 convenience 초기화를 호출하고, 호출된 convenience 초기화는 designated 초기화를 호출한다.
이것은 규칙 2와 3을 만족한다.
슈퍼클래스는 상위 슈퍼클래스를 가지지 않으므로, 규칙 1이 적용되지 않는다.
The subclass in this figure has two designated initializers and one convenience initializer. The convenience initializer must call one of the two designated initializers, because it can only call another initializer from the same class. This satisfies rules 2 and 3 from above. Both designated initializers must call the single designated initializer from the superclass, to satisfy rule 1 from above.
이 그림에서 서브클래스는 designated 초기화 두개와 convenience 초기화 하나를 가진다.
convenience 초기화는 반드시 두개의 designated 초기화중 하나를 호출해야 한다.
왜냐면 같은 클래스에 있는 다른 초기화만 호출할수 있기 때문이다.
이것은 규칙 2와 3을 만족한다.
두개의 designated 초기화는 슈퍼클래스의 designated 초기화를 하나 호출해야 해서, 규칙 1을 만족시켜야 한다.
NOTE
These rules don’t affect how users of your classes create instances of each class. Any initializer in the diagram above can be used to create a fully initialized instance of the class they belong to. The rules only affect how you write the implementation of the class’s initializers.
이 규칙은 클래스의 사용자가 각 클래스의 인스턴스를 어떻게 만드는지에는 영향을 미치지 않는다.
위의 다이어그램에 있는 모든 초기화는 클래스의 인스턴스를 완전히 초기화하는데 사용될수 있다.
규칙은 클래스의 초기화를 어떻게 구현하는지에만 영향을 미친다.
The figure below shows a more complex class hierarchy for four classes. It illustrates how the designated initializers in this hierarchy act as “funnel” points for class initialization, simplifying the interrelationships among classes in the chain:
아래의 그림은 4개의 클래스에서 좀더 복잡한 상속을 보여준다.
이 그림은 클래스 초기화에 대해 상속에서 designated 초기화가 어떻게 연결지점 역활을 하지는 보여준다.
Two-Phase Initialization
Class initialization in Swift is a two-phase process. In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.
swift에서 클래스 초기화는 2단계다.
첫단계는 클래스에 의해서 각 저장 속성의 초기값이 할당된다.
모든 저장속성에 대해 초기 단계가 끝나면, 두번째 단계가 시작되고,
각 클래스는 새로운 인스턴스가 사용 준비가 되기 전에 저장속성을 사용자정의 할수 있는 기회를 얻는다.
The use of a two-phase initialization process makes initialization safe, while still giving complete flexibility to each class in a class hierarchy. Two-phase initialization prevents property values from being accessed before they are initialized, and prevents property values from being set to a different value by another initializer unexpectedly.
2단계 초기화 처리를 사용하는 것은 초기화를 안전하게 만들어 주고, 클래스 상속에서 각 클래스에 완전히 유연함을 제공한다.
2단계 초기화는 속성 값이 초기화되기 전에 접근되는걸 막아주고,
속성 값이 다른 초기화에 의해 의도치않게 다른 값으로 설정되는것을 막아준다.
NOTE
Swift’s two-phase initialization process is similar to initialization in Objective-C. The main difference is that during phase 1, Objective-C assigns zero or null values (such as 0 or nil) to every property. Swift’s initialization flow is more flexible in that it lets you set custom initial values, and can cope with types for which 0 or nil is not a valid default value.
swift의 2단계 초기화 처리는 objective-c의 초기화와 비슷하다.
주요 다른점은 1단계 진행시, objective-c 는 0 또는 null 값을 모든 속성에 할당한다.
swift의 초기화 흐름은 더 유연해서 너가 초기 값을 마음대로 바꿀수 있게 해주고,
0 또는 nil 이 기본값으로 유효하지 않은 타입에 대처할수 있다.
Swift’s compiler performs four helpful safety-checks to make sure that two-phase initialization is completed without error:
swift의 컴파일러는 4가지 도움되는 안전검사를 수행해서 2단계 초기화가 에러 없이 완료되도록 확인한다.
Safety check 1
A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.
As mentioned above, the memory for an object is only considered fully initialized once the initial state of all of its stored properties is known. In order for this rule to be satisfied, a designated initializer must make sure that all of its own properties are initialized before it hands off up the chain.
designated 초기화는 슈퍼클래스 초기화에 위임하기 전에 반드시 자신의 모든 속성이 초기화되는지 확인한다.
위에서 언급했듯이, 객체에 대한 메모리는 모든 저장 속성이 초기 상태라고 알려졌을때 완전히 초기화 되었다고 간주한다.
이 규칙을 만족시키기 위해,
designated 초기화는 상위 연결고리로 떠나보내기 전에 자신의 모든 속성이 초기화 되도록 확인해야만 한다.
(모든 속성을 초기화 한 후에 슈퍼클래스 초기화로 위임해야 한다는 뜻 같다)
Safety check 2
A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn’t, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.
designated 초기화는 상속받은 속성에 값을 할당하기 전에 슈퍼클래스 초기화에 위임을 해야만 한다.
그러지 않으면 designated 초기화가 할당한 새로운 값이 슈퍼클래스에 의해서 덮어씌워질것이다.
Safety check 3
A convenience initializer must delegate to another initializer before assigning a value to any property (including properties defined by the same class). If it doesn’t, the new value the convenience initializer assigns will be overwritten by its own class’s designated initializer.
convenience 초기화는 반드시 어떤 속성에든 값을 할당하기 전에 다른 초기화에 위임해야 한다
(같은 클래스에서 정의된 속성도 포함해서)
그러지 않으면, convenience 초기화가 할당한 새로운 값이 같은 클래스의 designated 초기화에 의해 덮어씌워질것이다.
Safety check 4
An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.
초기화는 첫번째 단계 초기화가 끝나기 전에는
모든 인스턴스 메소드를 호출할수 없고, 모든 인스턴스 속성 값을 읽을수 없고, self 값을 참조할수 없다.
The class instance is not fully valid until the first phase ends. Properties can only be accessed, and methods can only be called, once the class instance is known to be valid at the end of the first phase.
클래스 인스턴스는 첫단계가 끝나기 전에는 완전히 유효하지 않다.
클래스 인스턴스가 첫번째 단계가 끝나고 유효하다고 알려지면, 그때서야 속성에 접근할수 있고, 메소드를 호출할수 있다.
Here’s how two-phase initialization plays out, based on the four safety checks above:
여기 위의 4단계 안전 검사를 기반으로 2단계 초기화가 어떻게 동작하는지 보자.
Phase 1
- A designated or convenience initializer is called on a class.
- Memory for a new instance of that class is allocated. The memory is not yet initialized.
- A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.
- The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.
- This continues up the class inheritance chain until the top of the chain is reached.
- Once the top of the chain is reached, and the final class in the chain has ensured that all of its stored properties have a value, the instance’s memory is considered to be fully initialized, and phase 1 is complete.
- designated 또는 convenience 초기화가 클래스에서 호출된다.
- 클래스의 새로운 인스턴스를 위한 메모리가 할당된다. 메모리는 아직 초기화되지 않았다.
- 클래스에 대한 designated 초기화는 모든 저장속성이 값을 가지게 된걸 확인한다.
저장속성들에 대한 메모리가 이제 초기화 되었다. - designated 초기화는 슈퍼클래스 초기화를 호출하여 자신의 저장속성에 같은 작업을 수행하도록 한다.
- 최상위 클래스에 도달할때까지 슈퍼클래스의 초기화를 이어서 호출한다.
- 최상위 클래스에 도달하고, 최상위 클래스가 모든 저장 속성이 값을 가진다는걸 확신하면,
인스턴스의 메모리는 완전히 초기화 되었다고 간주되며, 1단계가 끝난다.
Phase 2
- Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access self and can modify its properties, call its instance methods, and so on.
- Finally, any convenience initializers in the chain have the option to 수 the instance and to work with self.
- 최상위 클래스에서 아래로 내려오면서, 각 designated 초기화는 인스턴스를 더 사용자정의 할수 있다.
초기수는 이제 self 에 접근할수 있고, 자신의 속성을 수정할수 있고, 인스턴스 메소드를 호출할수 있다. 기타등등. - 마지막으로, 모든 convenicen 초기화가 self 를 사용하여 인스턴스를 사용자정의 할수 있다.
Here’s how phase 1 looks for an initialization call for a hypothetical subclass and superclass:
In this example, initialization begins with a call to a convenience initializer on the subclass. This convenience initializer cannot yet modify any properties. It delegates across to a designated initializer from the same class.
이 예제에서, 서브클래스의 convenience 초기화가 호출되어 초기화가 시작된다.
이 convenience 초기화는 아직 어떤 속성도 수정할수 없다.
같은 클래스의 designated 초기화로 위임된다.
The designated initializer makes sure that all of the subclass’s properties have a value, as per safety check 1. It then calls a designated initializer on its superclass to continue the initialization up the chain.
designated 초기화는 안전검사1에 따라서, 서브클래스의 모든 속성들이 값을 가진다는걸 확인한다.
그후 슈퍼클래스에 있는 designated 초기화를 호출해서 초기화 사슬을 계속 한다.
The superclass’s designated initializer makes sure that all of the superclass properties have a value. There are no further superclasses to initialize, and so no further delegation is needed.
슈퍼클래스의 desinaged 초기화는 슈퍼클래스의 모든 속성이 값을 가진다는걸 확인한다.
초기화할 슈퍼클래스가 더 없으니까, 추가적인 위임은 필요하지 않다.
As soon as all properties of the superclass have an initial value, its memory is considered fully initialized, and phase 1 is complete.
슈퍼클래스의 모든 속성이 초기 값을 가지게 되면, 이 메모리는 완전히 초기화 된걸로 간주되고, 1단계가 끝난다.
Here’s how phase 2 looks for the same initialization call:
The superclass’s designated initializer now has an opportunity to customize the instance further (although it does not have to).
슈퍼클래스의 designated 초기화는 인스턴스를 더 사용자정의할수 있는 기회를 갖는다 (그럴 필요가 없더라도)
Once the superclass’s designated initializer is finished, the subclass’s designated initializer can perform additional customization (although again, it does not have to).
슈퍼클래스의 designated 초기화가 끝나면, 서브클래스의 designated 초기화는 추가적인 사용자정의를 수행할수 있다.
(그럴 필요가 없더라도)
Finally, once the subclass’s designated initializer is finished, the convenience initializer that was originally called can perform additional customization.
마지막으로, 서브클래스의 designated 초기화가 끝나면,
원래 호출되었던 convenience 초기화가 추가적인 사용자정의를 수행 할수있다.
Initializer Inheritance and Overriding
Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default. Swift’s approach prevents a situation in which a simple initializer from a superclass is inherited by a more specialized subclass and is used to create a new instance of the subclass that is not fully or correctly initialized.
objective-c에서의 서브클래스와 다르게, swift 의 서브클래스는 기본적으로 슈퍼클래스의 초기화를 상속하지 않는다.
swift의 접근방식은 좀 더 특별한 서브클래스가 슈퍼클래스의 간단한 초기화를 상속하고
서브클래스의 새로운 인스턴스를 만들면 전체가 초기화되지 않거나 올바르게 초기화되지 않는걸 막아준다.
NOTE
Superclass initializers are inherited in certain circumstances, but only when it is safe and appropriate to do so. For more information, see Automatic Initializer Inheritance below.
슈퍼클래스 초기화는 안전하고 적절한 특정 상황에서만 상속된다. 추가정보는 아래를 봐라.
If you want a custom subclass to present one or more of the same initializers as its superclass, you can provide a custom implementation of those initializers within the subclass.
서브클래스가 슈퍼클래스와 같은 초기화를 하나 이상 제공하길 원하면,
서브클래스 안에서 그 초기화들의 사용자정의 구현을 만들어야 한다.
When you write a subclass initializer that matches a superclass designated initializer, you are effectively providing an override of that designated initializer. Therefore, you must write the override modifier before the subclass’s initializer definition. This is true even if you are overriding an automatically provided default initializer, as described in Default Initializers.
슈퍼클래스의 designated 초기화와 일치하는 초기화를 서브클래스에 작성할때,
그 designated 초기화의 override를 효과적으로 제공해야 한다.
그러므로 서브클래스의 초기화 정의 앞에 override를 적어야만 한다.
자동으로 제공되는 기본 초기화를 override 할때도 마찬가지다.
As with an overridden property, method or subscript, the presence of the override modifier prompts Swift to check that the superclass has a matching designated initializer to be overridden, and validates that the parameters for your overriding initializer have been specified as intended.
override 된 속성, 메소드, 첨자와 마찬가지로,
override 키워드는 슈퍼클래스가 일치하는 designated 초기화를 가지는지,
의도한대로 매개변수가 지정되었는지를 swift가 검사하게 한다.
NOTE
You always write the override modifier when overriding a superclass designated initializer, even if your subclass’s implementation of the initializer is a convenience initializer.
슈퍼클래스의 designated 초기화를 override 할때 항상 override를 써라.
서브클래스의 초기화가 covenience 초기화 일지라도.
Conversely, if you write a subclass initializer that matches a superclass convenience initializer, that superclass convenience initializer can never be called directly by your subclass, as per the rules described above in Initializer Delegation for Class Types. Therefore, your subclass is not (strictly speaking) providing an override of the superclass initializer. As a result, you do not write the override modifier when providing a matching implementation of a superclass convenience initializer.
거꾸로 말하면, 슈퍼클래스의 convenience 초기화와 일치하는 서브클래스 초기화를 작성하면,
슈퍼클래스의 convenience 초기화는 서브클래스에 의해서 결코 직접 호출되지 않는다. 위에서 언급한것처럼.
그러므로, 엄밀히 말하면 서브클래스는 슈퍼클래스 초기화의 override를 하는게 아니다.
결과적으로 슈퍼클래스 convenience 초기화와 일치하는 구현을 제공할때 override를 쓰지 마라.
The example below defines a base class called Vehicle. This base class declares a stored property called numberOfWheels, with a default Int value of 0. The numberOfWheels property is used by a computed property called description to create a String description of the vehicle’s characteristics:
아래 예제는 Vehicle 이라는 base 클래스를 정의한다.
이 base 클래스는 numberOfWheels 라는 저장속성을 선언하고, 기본값으로 0을 넣었다.
numberOfWheels 속성은 description 이라는 계산속성에 의해 사용되고, 운송수단의 특징을 기술하는 문자열을 만든다.
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
The Vehicle class provides a default value for its only stored property, and does not provide any custom initializers itself. As a result, it automatically receives a default initializer, as described in Default Initializers. The default initializer (when available) is always a designated initializer for a class, and can be used to create a new Vehicle instance with a numberOfWheels of 0:
Vehicle 클래스는 저장속성에만 기본값을 제공하고, 다른 사용자정의 초기화는 제공하지 않는다.
결과적으로, 자동으로 기본 초기화를 받는다.
기본 초기화는 항상 클래스에 대해 designated 초기화고,
numberOfWheels 가 0인 새로운 Vehicle 인스턴스를 만들때 사용할수 있다.
let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")
// Vehicle: 0 wheel(s)
The next example defines a subclass of Vehicle called Bicycle:
다음 예제는 Vehicle의 서브클래스 Bicycle을 정의한다.
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
The Bicycle subclass defines a custom designated initializer, init(). This designated initializer matches a designated initializer from the superclass of Bicycle, and so the Bicycle version of this initializer is marked with the override modifier.
Bicycle 서브클래스는 사용자정의 designated 초기화, init() 을 정의한다.
이 designated 초기화는 Bicycle 의 슈퍼클래스에 있는 designated 초기화와 매칭되고,
이 초기화의 Bicycle 버젼은 override 표시가 쓰여 있다.
The init() initializer for Bicycle starts by calling super.init(), which calls the default initializer for the Bicycle class’s superclass, Vehicle. This ensures that the numberOfWheels inherited property is initialized by Vehicle before Bicycle has the opportunity to modify the property. After calling super.init(), the original value of numberOfWheels is replaced with a new value of 2.
Bicycle의 init() 초기화는 super.init()을 호출하면서 시작하고,
super.init()은 Bicycle 클래스의 슈퍼클래스인 Vehicle 의 기본 초기화를 호출한다.
이것은 Bicycle이 이 속성을 수정하기 전에 상속받은 numberOfWheels 속성이 Vehicle에의해 초기화되는걸 보장한다.
super.init()을 호출한 후에, numberOfWheels 의 원래값은 새로운 값 2로 대체된다.
If you create an instance of Bicycle, you can call its inherited description computed property to see how its numberOfWheels property has been updated:
Bicycle의 인스턴스를 만들면, 상속받은 계산속성 description을 호출해서 numberOfWheels 속성이 어떻게 수정되었는지 볼수 있다.
let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")
// Bicycle: 2 wheel(s)
If a subclass initializer performs no customization in phase 2 of the initialization process, and the superclass has a zero-argument designated initializer, you can omit a call to super.init() after assigning values to all of the subclass’s stored properties.
서브클래스 초기화가 초기화 과정의 2단계에서 아무런 사용자정의를 수행하지 않고,
슈퍼클래스가 매개변수없는 designated 초기화를 가지면,
서브클래스의 모든 저장속성에 값을 할당한후 super.init() 호출을 생략할수 있다.
This example defines another subclass of Vehicle, called Hoverboard. In its initializer, the Hoverboard class sets only its color property. Instead of making an explicit call to super.init(), this initializer relies on an implicit call to its superclass’s initializer to complete the process.
이 예제는 Vehicle 의 또다른 서브클래스 Hoverboard 를 정의한다.
이것의 초기화에서, Hoverboard 클래스는 자신의 color 속성만을 설정한다.
명시적으로 super.init() 호출 대신에, 슈퍼클래스 초기화의 암시적인 호출에 의존해 처리를 완료한다.
class Hoverboard: Vehicle {
var color: String
init(color: String) {
self.color = color
// super.init() implicitly called here
}
override var description: String {
return "\(super.description) in a beautiful \(color)"
}
}
An instance of Hoverboard uses the default number of wheels supplied by the Vehicle initializer.
Hoverboard 의 인스턴스는 Vehicle 초기화에 의해 기본 바퀴의 갯수를 사용한다.
let hoverboard = Hoverboard(color: "silver")
print("Hoverboard: \(hoverboard.description)")
// Hoverboard: 0 wheel(s) in a beautiful silver
NOTE
Subclasses can modify inherited variable properties during initialization, but can not modify inherited constant properties.
서브클래스는 초기화 과정중 상속받은 변수 속성을 수정할수 있지만, 상속받은 상수 속성은 수정할수 없다.
Automatic Initializer Inheritance
As mentioned above, subclasses do not inherit their superclass initializers by default. However, superclass initializers are automatically inherited if certain conditions are met. In practice, this means that you do not need to write initializer overrides in many common scenarios, and can inherit your superclass initializers with minimal effort whenever it is safe to do so.
위에서 언급했듯이, 서브클래스 기본적으로 슈퍼클래스 초기화를 상속하지 않는다.
하지만 특정조건이 충족되면 슈퍼클래스 초기화는 자동으로 상속된다.
실제로, 이것은 많은 일반적인 상황에서 초기화 override를 쓸 필요가 없다는걸 뜻하고,
안전할때마다 최소한의 노력으로 슈퍼클래스 초기화를 상속할수 있다는걸 뜻한다.
Assuming that you provide default values for any new properties you introduce in a subclass, the following two rules apply:
서브클래스에서 생성한 모든 새로운 속성에 대해 기본값을 제공하는걸 가정할때, 다음의 두가지 규칙이 적용된다.
Rule 1
If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
만약 서브클래스가 designated 초기화를 전혀 정의하지 않았으면, 슈퍼클래스의 모든 designated 초기화가 자동으로 상속된다.
Rule 2
If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.
만약 서브클래스가 슈퍼클래스의 모든 designated 초기화를 구현하면
- 규칙1에 따라 상속 받거나 또는 사용자정의 구현을 했을때 -
슈퍼클래스의 모든 convenience 초기화가 자동으로 상속된다.
These rules apply even if your subclass adds further convenience initializers.
이 규칙들은 서브클래스가 convenience 초기화를 더 추가하더라도 적용된다.
NOTE
A subclass can implement a superclass designated initializer as a subclass convenience initializer as part of satisfying rule 2.
서브클래스는 규칙2 만족의 일부로 슈퍼클래스의 designated 초기화를 서브클래스의 convenience 초기화로 구현할수 있다
Designated and Convenience Initializers in Action
The following example shows designated initializers, convenience initializers, and automatic initializer inheritance in action. This example defines a hierarchy of three classes called Food, RecipeIngredient, and ShoppingListItem, and demonstrates how their initializers interact.
아래 예제는 designated 초기화, convenience 초기화, 자동 초기화 상속의 작동을 보여준다.
이 예제는 Food, ReceipeIngredient, ShoppingListItem 이라는 클래스 3개의 계층구조를 정의하고,
어떻게 그들의 초기화가 상호작용하는지 보여준다.
The base class in the hierarchy is called Food, which is a simple class to encapsulate the name of a foodstuff. The Food class introduces a single String property called name and provides two initializers for creating Food instances:
계층구조에서 기본 클래스는 Food 이고, 음식의 이름을 감싸는 간단한 클래스다.
Food 클래스는 name 이라는 하나의 문자열 속성을 제공하고, Food 인스턴스를 만들기 위한 두개의 초기화를 제공한다.
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
The figure below shows the initializer chain for the Food class:
아래 그림은 Food 클래스의 초기화 연결고리를 보여준다.
Classes do not have a default memberwise initializer, and so the Food class provides a designated initializer that takes a single argument called name. This initializer can be used to create a new Food instance with a specific name:
클래스는 기본 멤버단위 초기화를 가지고 있지 않고, 하나의 name 매개변수를 취하는 designated 초기화를 제공한다.
이 초기화는 특정한 이름으로 새로운 Food 인스턴스를 만드는데 사용된다.
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"
The init(name: String) initializer from the Food class is provided as a designated initializer, because it ensures that all stored properties of a new Food instance are fully initialized. The Food class does not have a superclass, and so the init(name: String) initializer does not need to call super.init() to complete its initialization.
Food 클래스의 init(name: String) 초기화는 designated 초기화로써 제공된다.
왜냐면 새로운 Food 인스턴스의 모든 저장속성을 완전히 초기화하는걸 보증하기 때문에.
Food 클래스는 슈퍼클래스를 가지지 않아서, init(name: String) 초기화는 초기화를 완료하기 위해 super.init()을 호출할 필요가 없다.
The Food class also provides a convenience initializer, init(), with no arguments. The init() initializer provides a default placeholder name for a new food by delegating across to the Food class’s init(name: String) with a name value of [Unnamed]:
Food 클래스는 또한 convenience 초기화도 제공한다. 매개변수 없는 init().
init() 초기화는 새로운 음식의 기본 이름으로 [Unnamed] 를 제공해서 init(name: String) 메소드에 위임한다.
let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]"
The second class in the hierarchy is a subclass of Food called RecipeIngredient. The RecipeIngredient class models an ingredient in a cooking recipe. It introduces an Int property called quantity (in addition to the name property it inherits from Food) and defines two initializers for creating RecipeIngredient instances:
이 계층구조의 두번째 클래스는 ReceipeIngredient 라는 Food의 서브클래스다.
ReceipeIngredient 클래스는 요리 조리의 재료를 모델링 한다.
quantity 라는 정수형 속성을 제공하고 (Food 에서 상속받은 name 속성에 더하여)
ReceipeIngredient 인스턴스 생성을 위한 두개의 초기하를 정의 한다.
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
The figure below shows the initializer chain for the RecipeIngredient class:
아래 그림은 ReceipeIngredient 클래스의 초기화 연결고리를 보여준다.
The RecipeIngredient class has a single designated initializer, init(name: String, quantity: Int), which can be used to populate all of the properties of a new RecipeIngredient instance. This initializer starts by assigning the passed quantity argument to the quantity property, which is the only new property introduced by RecipeIngredient. After doing so, the initializer delegates up to the init(name: String) initializer of the Food class. This process satisfies safety check 1 from Two-Phase Initialization above.
RecipeIngredient 클래스는 하나의 designated 초기화를 가지는데, init(name: String, quantity: Int) 이고,
이것은 새로운 RecipeIngredient 인스턴스의 모든 속성을 채우는데 사용된다.
이 초기화는 quantity 매개변수를 quantity 속성(RecipeIngredient에서 새로 소개된 유일한 속성)에 할당하는걸로 시작한다.
그러고 나서, 초기화는 Food 클래스의 init(name: String) 초기화에 위임한다.
이 처리는 위의 안전검사 1을 만족시킨다.
RecipeIngredient also defines a convenience initializer, init(name: String), which is used to create a RecipeIngredient instance by name alone. This convenience initializer assumes a quantity of 1 for any RecipeIngredient instance that is created without an explicit quantity. The definition of this convenience initializer makes RecipeIngredient instances quicker and more convenient to create, and avoids code duplication when creating several single-quantity RecipeIngredient instances. This convenience initializer simply delegates across to the class’s designated initializer, passing in a quantity value of 1.
RecipeIngredient 는 또한 convenience 초기화를 정의하는데, init(name: String) 이고
이름만 가지고 RecipeIngredient 인스턴스를 생성할때 사용된다.
이 convenience 초기화는 명백한 수량없이 생성되는 모든 RecipeIngredient 인스턴스에 quantity가 1이라고 간주한다.
convenience 초기화는 하나의 수량을 가지는 RecipeIngredient 인스턴스를 여러개 만들때 코드 중복을 피하고,
빠르고, 더 편하게 생성하도록 만들어준다.
이 convenience 초기화는 단순히 클래스의 designated 초기화에 수량을 1로 전달하여 위임한다.
The init(name: String) convenience initializer provided by RecipeIngredient takes the same parameters as the init(name: String) designated initializer from Food. Because this convenience initializer overrides a designated initializer from its superclass, it must be marked with the override modifier (as described in Initializer Inheritance and Overriding).
RecipeIngredient 의 init(name: String) convenience 초기화는 Food 의 designated 초기화와 동일한 매개변수를 갖는다.
이 convenience 초기화는 슈퍼클래스의 designated 초기화를 오버라이드 했으니까,
override 키워드를 꼭 써야 만 한다.
Even though RecipeIngredient provides the init(name: String) initializer as a convenience initializer, RecipeIngredient has nonetheless provided an implementation of all of its superclass’s designated initializers. Therefore, RecipeIngredient automatically inherits all of its superclass’s convenience initializers too.
RecipeIngredient 가 init(name: String) convenience 초기화를 제공하더라고,
RecipeIngredient 는 슈퍼클래스의 모든 designated 초기화를 구현했다.
그러므로 RecipeIngredient 는 자동으로 슈퍼클래스의 모든 convenience 초기화를 상속한다.
In this example, the superclass for RecipeIngredient is Food, which has a single convenience initializer called init(). This initializer is therefore inherited by RecipeIngredient. The inherited version of init() functions in exactly the same way as the Food version, except that it delegates to the RecipeIngredient version of init(name: String) rather than the Food version.
이 예제에서, RecipeIngredient의 슈퍼클래스는 Food 고, Food 는 하나의 convenience 초기화 init()을 가지고 있다.
이 초기화는 RecipeIngredient 에 상속되었다.
Food 버젼 대신에 RecipeIngredient 버젼의 init(name: String) 에 위임한다는것만 빼고는,
상속된 init() 버젼은 Food 버젼과 완전히 동일하다.
All three of these initializers can be used to create new RecipeIngredient instances:
이 초기화 3개는 모두 RecipeIngredient 인스턴스를 생성하는데 사용될수 있다.
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
The third and final class in the hierarchy is a subclass of RecipeIngredient called ShoppingListItem. The ShoppingListItem class models a recipe ingredient as it appears in a shopping list.
3번째, 계층구조의 마지막 클래스는 RecipeIngredient 의 서브클래스 ShoppingListItem 이다.
ShoppingListItem 는 쇼핑리스트에서 요리 재료를 모델링한다.
Every item in the shopping list starts out as “unpurchased”. To represent this fact, ShoppingListItem introduces a Boolean property called purchased, with a default value of false. ShoppingListItem also adds a computed description property, which provides a textual description of a ShoppingListItem instance:
쇼핑리스트의 모든 아이템은 "미구매" 로 시작된다.
이것을 표현하기 위하여, ShoppingListItem 은 purchased 라는 Boolean 속성을 가지고 있고, 기본값은 false 다.
ShoppingListItem 는 또한 description 계산속성을 가지고 있고, ShoppingListItem 인스턴스의 문자형 설명을 제공한다.
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
}
NOTE
ShoppingListItem does not define an initializer to provide an initial value for purchased, because items in a shopping list (as modeled here) always start out unpurchased.
ShoppingListItem 은 purchased 에 대한 초기값을 제공하는 초기화를 정의하지 않았다.
왜냐면 쇼핑 리스트의 아이템들은 항상 미구매로 시작하니까.
Because it provides a default value for all of the properties it introduces and does not define any initializers itself, ShoppingListItem automatically inherits all of the designated and convenience initializers from its superclass.
The figure below shows the overall initializer chain for all three classes:
모든 속성에 대해 기본값을 제공하고, 어떤 초기화도 정의하지 않았으므로,
ShoppingListItem 은 자동으로 모든 designated 와 convenience 초기화를 슈퍼클래스로부터 상속받는다.
아래 그림은 계층구조의 모든 초기화를 보여준다.
You can use all three of the inherited initializers to create a new ShoppingListItem instance:
ShoppingListItem 인스턴스를 만들기 위해 상속받은 3개의 초기화를 모두 사용할수 있다.
var breakfastList = [
ShoppingListItem(),
ShoppingListItem(name: "Bacon"),
ShoppingListItem(name: "Eggs", quantity: 6),
]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
print(item.description)
}
// 1 x Orange juice ✔
// 1 x Bacon ✘
// 6 x Eggs ✘
Here, a new array called breakfastList is created from an array literal containing three new ShoppingListItem instances. The type of the array is inferred to be [ShoppingListItem]. After the array is created, the name of the ShoppingListItem at the start of the array is changed from "[Unnamed]" to "Orange juice" and it is marked as having been purchased. Printing the description of each item in the array shows that their default states have been set as expected.
여기 breakfastList 라는 새로운 배열이 3개의 ShoppingListItem 인스턴스를 가지고 생성되었다.
배열의 타입은 [ShoppingListItem] 로 추정된다.
배열이 생성된 후에, 배열의 처음에 있는 ShoppingListItem 의 이름이 "[Unnamed]" 에서 "Orange juice" 로 변경되고,
purchased가 true로 된다.
배열에 있는 각 아이템의 description을 출력하면 그것들의 기본 상태가 예상대로 설정되었음을 볼수 있다.
Failable Initializers
It is sometimes useful to define a class, structure, or enumeration for which initialization can fail. This failure might be triggered by invalid initialization parameter values, the absence of a required external resource, or some other condition that prevents initialization from succeeding.
클래스, 구조체, 열거형에 실패할수 있는 초기화를 정의하는게 가끔 유용할때가 있다.
이 실패는 적절하지 않은 초기화 매개변수 값이나, 필요한 외부 리소스가 없을때,
또는 성공적인 초기화를 방해하는 다른 조건에 의해서 발생할수 있다.
To cope with initialization conditions that can fail, define one or more failable initializers as part of a class, structure, or enumeration definition. You write a failable initializer by placing a question mark after the init keyword (init?).
실패할수 있는 초기화 조건에 대응하기 위해서, 클래스, 구조체, 열거형에 하나 이상의 실패가능한 초기화를 정의해라.
init? 으로 실패가능한 초기화를 작성할수 있다.
NOTE
You cannot define a failable and a nonfailable initializer with the same parameter types and names.
실패가능과 실패불가능 초기화 2개를 같은 이름과 같은 매개변수 타입으로 정의할수 없다.
A failable initializer creates an optional value of the type it initializes. You write return nil within a failable initializer to indicate a point at which initialization failure can be triggered.
실패가능한 초기화는 초기화 타입의 optional 값을 만든다.
초기화가 실패했을때 nil 을 리턴 해라.
NOTE
Strictly speaking, initializers do not return a value. Rather, their role is to ensure that self is fully and correctly initialized by the time that initialization ends. Although you write return nil to trigger an initialization failure, you do not use the return keyword to indicate initialization success.
엄밀히 말하면, 초기화는 값을 리턴하지 않는다.
대신 초기화의 역활은 초기화가 끝났을때 스스로를 완전히 올바르게 초기화가 되는것을 보장하는 것이다.
비록 초기화의 실패를 나타내기 위해서 nil을 리턴하지만,
초기화의 성공을 나타내기 위해서는 return 키워드를 사용하지 마라.
For instance, failable initializers are implemented for numeric type conversions. To ensure conversion between numeric types maintains the value exactly, use the init(exactly:)initializer. If the type conversion cannot maintain the value, the initializer fails.
예를 들어, 실패가능한 초기화는 숫자타입변환으로 구현되었다.
숫자타입간에 값을 정확히 유지하려면, init(exactly:) 초기화를 사용해라.
타입 변환이 값을 유지할수 없으면, 초기화는 실패한다.
let wholeNumber: Double = 12345.0
let pi = 3.14159
if let valueMaintained = Int(exactly: wholeNumber) {
print("\(wholeNumber) conversion to Int maintains value of \(valueMaintained)")
}
// Prints "12345.0 conversion to Int maintains value of 12345"
let valueChanged = Int(exactly: pi)
// valueChanged is of type Int?, not Int
if valueChanged == nil {
print("\(pi) conversion to Int does not maintain value")
}
// Prints "3.14159 conversion to Int does not maintain value"
The example below defines a structure called Animal, with a constant String property called species. The Animal structure also defines a failable initializer with a single parameter called species. This initializer checks if the species value passed to the initializer is an empty string. If an empty string is found, an initialization failure is triggered. Otherwise, the species property’s value is set, and initialization succeeds:
아래 예제는 Animal 이라는 구조체를 species라는 상수 문자열 속성을 가지고 정의하였다.
Animal 구조체는 또한 species라는 매개변수 하나인 실패가능한 초기화를 정의하였다.
이 초기화는 초기화에 넘어온 species 값이 빈 문자열인지 검사한다.
만약 빈 문자열이면 초기화는 실패한다.
아니면 species 속성값이 설정되고 초기화는 성공한다.
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
You can use this failable initializer to try to initialize a new Animal instance and to check if initialization succeeded:
새로운 Animal 인스턴스를 만들고 초기화가 성공했는지 검사하기 위해서 이 실패가능한 초기화를 사용할수 있다.
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
// Prints "An animal was initialized with a species of Giraffe"
If you pass an empty string value to the failable initializer’s species parameter, the initializer triggers an initialization failure:
실패가능한 초기화의 species 매개변수에 빈 문자열을 넘기면, 초기화는 실패한다.
let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?, not Animal
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// Prints "The anonymous creature could not be initialized"
NOTE
Checking for an empty string value (such as "" rather than "Giraffe") is not the same as checking for nil to indicate the absence of an optional String value. In the example above, an empty string ("") is a valid, non-optional String. However, it is not appropriate for an animal to have an empty string as the value of its species property. To model this restriction, the failable initializer triggers an initialization failure if an empty string is found.
빈문자열 검사 ("Giraffe" 대신 "")는 optional String 값이 없음을 나타내는 nil 검사와 동일하지 않다.
위의 예에서, 빈문자열 ("")은 유효한 non-optional 문자열이다.
그렇더라도 species 속성이 빈문자열을 가지는 동물은 적절하지 않다.
이 제한을 모델링하기 위해, 실패가능한 초기화는 빈문자열이 발견되면 실패를 발생시킨다.
Failable Initializers for Enumerations
You can use a failable initializer to select an appropriate enumeration case based on one or more parameters. The initializer can then fail if the provided parameters do not match an appropriate enumeration case.
하나 이상의 매개변수를 기반으로 적절한 열거형 case 를 찾기 위해 실패가능한 초기화를 사용할수 있다.
제공된 매개변수가 적절한 열거형 case 를 찾기 못하면 초기화는 실패할수 있다.
The example below defines an enumeration called TemperatureUnit, with three possible states (kelvin, celsius, and fahrenheit). A failable initializer is used to find an appropriate enumeration case for a Character value representing a temperature symbol:
아래 예제는 TemperatureUnit 이라는 열거형을 정의하였고, 3가지 가능한 상태 (kelvein, celsius, fahrenheit) 를 다룰수 있다.
실패가능한 초기화는 온도 표시를 표현하는 문자 값에 대해서 적절한 열거형 case를 찾는데 사용된다.
enum TemperatureUnit {
case kelvin, celsius, fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .kelvin
case "C":
self = .celsius
case "F":
self = .fahrenheit
default:
return nil
}
}
}
You can use this failable initializer to choose an appropriate enumeration case for the three possible states and to cause initialization to fail if the parameter does not match one of these states:
이 실패가능한 초기화를 3가지 가능한 상태에서 적절한 열거형 case를 찾는 경우와
적절한 상태를 찾지 못했을때 실패를 발생시키기 위해서 사용할수 있다.
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
Failable Initializers for Enumerations with Raw Values
Enumerations with raw values automatically receive a failable initializer, init?(rawValue:), that takes a parameter called rawValue of the appropriate raw-value type and selects a matching enumeration case if one is found, or triggers an initialization failure if no matching value exists.
열거형의 raw값은 자동으로 실패가능한 초기화를 받는다.
init?(rawValue:) 는 rawValue 라는 매개변수를 받는데 열거형의 case 중 하나와 일치하는걸 찾으면 그 값을 선택하고
일치하는걸 못찾으면 실패를 발생시킨다.
You can rewrite the TemperatureUnit example from above to use raw values of type Character and to take advantage of the init?(rawValue:) initializer:
위의 예제 TemperatureUnit 을 문자타입의 raw 값을 사용하도록 다시 작성할수 있고
init?(rawValue:) 초기화의 이점을 가질수 있다.
enum TemperatureUnit: Character {
case kelvin = "K", celsius = "C", fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
Propagation of Initialization Failure
A failable initializer of a class, structure, or enumeration can delegate across to another failable initializer from the same class, structure, or enumeration. Similarly, a subclass failable initializer can delegate up to a superclass failable initializer.
클래스, 구조체, 열거형의 실패가능한 초기화는 같은 클래스, 구조체, 열거형의 다른 실패가능한 초기화에 위임할수 있다.
비슷하게 서브클래스의 실패가능한 초기화는 슈퍼클래스의 실패가능한 초기화에 위임할수 있다.
In either case, if you delegate to another initializer that causes initialization to fail, the entire initialization process fails immediately, and no further initialization code is executed.
두 경우다, 다른 초기화에 위임했는데 그 초기화에서 실패가 발생하면, 전체 초기화 처리는 즉시 실패하고,
초기화 코드의 나머지는 실행되지 않는다.
NOTE
A failable initializer can also delegate to a nonfailable initializer. Use this approach if you need to add a potential failure state to an existing initialization process that does not otherwise fail.
실패가능한 초기화는 실패가능하지않는 초기화에도 위임할수 있다.
이런 방식으로 기존의 실패하지 않는 초기화 처리에 잠새적인 처리 상태를 추가할수 있다.
The example below defines a subclass of Product called CartItem. The CartItem class models an item in an online shopping cart. CartItem introduces a stored constant property called quantity and ensures that this property always has a value of at least 1:
아래 예제는 CartItem 이라는 Product의 서브클래스를 정의한다.
CartItem 은 온라인쇼핑카트에 있는 아이템을 모델링한다.
CartItem 은 quantity 라는 저장상수속성을 가지고 있고, 이 속성이 항상 적어도 1의 값을 가지도록 보장한다.
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
class CartItem: Product {
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity
super.init(name: name)
}
}
The failable initializer for CartItem starts by validating that it has received a quantity value of 1 or more. If the quantity is invalid, the entire initialization process fails immediately and no further initialization code is executed. Likewise, the failable initializer for Product checks the name value, and the initializer process fails immediately if name is the empty string.
CartItem의 실패가능한 초기화는 넘겨받은 quantity 값이 1보다 큰지 검사부터 시작한다.
만약 quantity가 유효하지 않으면, 전체 초기화 처리는 즉시 실패하고, 나머지 초기화 코드는 실행되지 않는다.
마찬가지로, Product의 실패가능한 초기화는 name 값을 검사하고, name이 빈문자열이면 초기화 처리는 즉시 실패한다.
If you create a CartItem instance with a nonempty name and a quantity of 1 or more, initialization succeeds:
CartItem 인스턴스를 빈문자열이 아닌 이름과 하나 이상의 수량으로 만들면, 초기화는 성공한다.
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// Prints "Item: sock, quantity: 2"
If you try to create a CartItem instance with a quantity value of 0, the CartItem initializer causes initialization to fail:
CartItem 인스턴스를 수량 0으로 만들면, CartItem 초기화는 실패를 발생시킨다.
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
}
// Prints "Unable to initialize zero shirts"
Similarly, if you try to create a CartItem instance with an empty name value, the superclass Product initializer causes initialization to fail:
비슷하게, CartItem 인스턴스를 빈문자열로 만들면, 슈퍼클래스인 Product 초기화는 실패를 발생시킨다.
if let oneUnnamed = CartItem(name: "", quantity: 1) {
print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
// Prints "Unable to initialize one unnamed product"
Overriding a Failable Initializer
You can override a superclass failable initializer in a subclass, just like any other initializer. Alternatively, you can override a superclass failable initializer with a subclass nonfailable initializer. This enables you to define a subclass for which initialization cannot fail, even though initialization of the superclass is allowed to fail.
다른 초기화들처럼, 서브클래스에서 슈퍼클래스의 실패가능한 초기화를 override 할수 있다.
또는 슈퍼클래스의 실패가능한 초기화를 서브클래스에서 실패가능하지않은 초기화로 override 할수 있다.
이것은 비록 슈퍼클래스의 초기화가 실패를 허용하더라도, 실패할수없는 초기화를 가진 서브클래스를 정의할수 있게 해준다.
Note that if you override a failable superclass initializer with a nonfailable subclass initializer, the only way to delegate up to the superclass initializer is to force-unwrap the result of the failable superclass initializer.
슈퍼클래스의 실패가능한 초기화를 서브클래스에서 실패하지않는 초기화로 override 할때,
슈퍼클래스 초기화로 위임하는 유일한 방법은 서브클래스의 실패가능한 초기화의 결과를 강제로 unwrap 하는 것이다. (??? 뭔소리래?)
NOTE
You can override a failable initializer with a nonfailable initializer but not the other way around.
실패가능한 초기화를 실패하지않는 초기화로 override 할수 있지만, but not the other way around. (???)
The example below defines a class called Document. This class models a document that can be initialized with a name property that is either a nonempty string value or nil, but cannot be an empty string:
아래 예제는 Document라는 클래스를 정의한다.
이 클래스는 name 속성을 가지는 문서를 모델링하는데, name 속성은 nil 이나 문자열로 초기화될수 있지만, 빈문자열로는 초기화될수 없다.
class Document {
var name: String?
// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a nonempty name value
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
The next example defines a subclass of Document called AutomaticallyNamedDocument. The AutomaticallyNamedDocument subclass overrides both of the designated initializers introduced by Document. These overrides ensure that an AutomaticallyNamedDocumentinstance has an initial name value of "[Untitled]" if the instance is initialized without a name, or if an empty string is passed to the init(name:) initializer:
다음 예제는 AutomaticallyNamedDocument 라는 Document 의 서브클래스를 정의한다.
AutomaticallyNamedDocument 서브클래스는 Document에 있는 designated 초기화들를 override 한다.
만약 인스턴스가 이름없이 초기화되었거나,
init(name:) 초기화에 빈문자열이 전달되면,
이 override들은 AutomaticallyNamedDocumentinstance 가 name 에 "[Untitled]" 라는 초기값을 가지도록 보장해준다.
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
The AutomaticallyNamedDocument overrides its superclass’s failable init?(name:)initializer with a nonfailable init(name:) initializer. Because AutomaticallyNamedDocument copes with the empty string case in a different way than its superclass, its initializer does not need to fail, and so it provides a nonfailable version of the initializer instead.
AutomaticallyNamedDocument 는 슈퍼클래스의 실패가능한 init?(name:) 초기화를 override 해서
실패하지않는 init(name:) 초기화로 만든다.
왜냐하면 AutomaticallyNamedDocument는 빈문자열인 경우 슈퍼클래스와 다른방법으로 대처하기 때문에,
이 초기화를 실패처리가 필요하지 않아서, 실패하지않는 초기화 버젼을 제공한다.
You can use forced unwrapping in an initializer to call a failable initializer from the superclass as part of the implementation of a subclass’s nonfailable initializer. For example, the UntitledDocument subclass below is always named "[Untitled]", and it uses the failable init(name:) initializer from its superclass during initialization.
서브클래스의 실패하지않는 초기화안에서, 슈퍼클래스의 실패가능한 초기화를 호출하기 위해
강제로 unwrapping 할수 있다.
class UntitledDocument: Document {
override init() {
super.init(name: "[Untitled]")!
}
}
In this case, if the init(name:) initializer of the superclass were ever called with an empty string as the name, the forced unwrapping operation would result in a runtime error. However, because it’s called with a string constant, you can see that the initializer won’t fail, so no runtime error can occur in this case.
이 경우, 슈퍼클래스의 init(name:) 초기화는 name에 빈문자열을 넣어서 호출할때마다,
강제 unwrapping 은 런타임에러가 발생한다.
그럼에도 불구하고, 이것은 문자열 상수로 호출되기때문에, 초기화는 실패하지 않고, 런타임에러가 발생하는 경우는 없다.
The init! Failable Initializer
You typically define a failable initializer that creates an optional instance of the appropriate type by placing a question mark after the init keyword (init?). Alternatively, you can define a failable initializer that creates an implicitly unwrapped optional instance of the appropriate type. Do this by placing an exclamation mark after the init keyword (init!) instead of a question mark.
일반적으로 optional 인스턴스를 생성하는 실패가능한 초기화를 정의할수 있다. init? 키워드로.
대신에, 암시적으로 unrwapped 되는 optional 인스턴스를 생성하는 실패가능한 초기화를 정의할수 있다. init! 키워드로
You can delegate from init? to init! and vice versa, and you can override init? with init! and vice versa. You can also delegate from init to init!, although doing so will trigger an assertion if the init! initializer causes initialization to fail.
init? 에서 init! 으로 위임할수 있고 반대도 가능하고,
init? 을 init! 으로 override 할수 있고 반대도 가능하다.
init 에서 init! 으로 위임할수 있지만, init! 초기화가 실패하면 assertion이 발생한다.
Required Initializers
Write the required modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer:
클래스 초기화 선언 앞에 required 를 적어서 모든 서브클래스가 반드시 초기화를 구현해야 한다는걸 표시할수 있다.
class SomeClass {
required init() {
// initializer implementation goes here
}
}
You must also write the required modifier before every subclass implementation of a required initializer, to indicate that the initializer requirement applies to further subclasses in the chain. You do not write the override modifier when overriding a required designated initializer:
모든 서브클래스에서 required 초기화 구현앞에 required 를 붙여서,
계층구조에서 다음 서브클래스에 요구사항이 적용되도록 표시 해야만 한다.
required designated 초기화를 override 할때는 override 키워드를 쓰지 않는다.
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
NOTE
You do not have to provide an explicit implementation of a required initializer if you can satisfy the requirement with an inherited initializer.
상속받은 초기화가 요구사항을 만족시키면, 명시적으로 required 초기화를 꼭 구현할 필요는 없다.
Setting a Default Property Value with a Closure or Function
If a stored property’s default value requires some customization or setup, you can use a closure or global function to provide a customized default value for that property. Whenever a new instance of the type that the property belongs to is initialized, the closure or function is called, and its return value is assigned as the property’s default value.
만약 저장속성의 기본값이 약간의 사용자지정이나 설정이 요구되면,
closure나 전역함수를 사용해서 그 속성의 기본값을 사용자지정할수 있다.
그 속성이 속해있는 새로운 인스턴스가 초기화될때마다,
그 closure나 함수는 호출되고, 리턴값은 그 속성의 기본값으로 할당된다.
These kinds of closures or functions typically create a temporary value of the same type as the property, tailor that value to represent the desired initial state, and then return that temporary value to be used as the property’s default value.
이러한 closure나 함수는 일반적으로 그 속성에 대해서 같은 타입의 임시 값을 생성하고,
원하는 초기 상태를 표현하도록 조정하고, 속성의 기본값으로 사용되도록 임시값을 리턴한다.
Here’s a skeleton outline of how a closure can be used to provide a default property value:
여기 클로저를 기본 속성값을 제공하는데 사용할수 있는 기본 뼈대가 있다.
class SomeClass {
let someProperty: SomeType = {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
Note that the closure’s end curly brace is followed by an empty pair of parentheses. This tells Swift to execute the closure immediately. If you omit these parentheses, you are trying to assign the closure itself to the property, and not the return value of the closure.
클로저의 끝에는 빈 괄호가 따라옴을 주목해라.
이것은 swift가 그 클로저를 즉시 실행하도록 시킨다.
이 빈괄호를 생략하면, 속성에 클로저 자체를 할당하는것이고, 클로저의 리턴값을 할당하는게 아니다.
NOTE
If you use a closure to initialize a property, remember that the rest of the instance has not yet been initialized at the point that the closure is executed. This means that you cannot access any other property values from within your closure, even if those properties have default values. You also cannot use the implicit self property, or call any of the instance’s methods.
속성을 초기화하는데 클로저를 사용하면,
클로저가 실행되는 시점에서 인스턴의 나머지 부분은 아직 초기화되지 않았다는걸 명심해라.
이것은 클로저안에서 다른 속성값에 접근할수 없다는걸 뜻한다. 비록 그 속성들이 기본값을 가지고 있더라도.
또한 암시적인 self 속성을 사용할수 없고, 다른 인스턴스 메소드도 호출할수 없다.
The example below defines a structure called Chessboard, which models a board for the game of chess. Chess is played on an 8 x 8 board, with alternating black and white squares.
아래 예제는 Chessboard 라는 구조체를 정의해서, 체스게임판을 모델링한다.
체스는 검은색과 흰색이 번갈아 있는 8 x 8 칸의 판에서 게임한다.
To represent this game board, the Chessboard structure has a single property called boardColors, which is an array of 64 Bool values. A value of true in the array represents a black square and a value of false represents a white square. The first item in the array represents the top left square on the board and the last item in the array represents the bottom right square on the board.
이 게임판을 표현하기 위해서, Chessboard 구조체는 하나의 속성 boardColors 를 가지고, 그 속성은 64개의 Bool 값 배열이다.
이 배열에서 true 값은 검은색 사각형을 나타내고 false 값은 흰색 사각형을 나타낸다.
이 배열의 첫번째 아이템은 판의 좌상단 사각형을 나타내고 배열의 마지막 아이템은 판의 우하단 사각형을 나타낸다.
The boardColors array is initialized with a closure to set up its color values:
boardColors 배열은 클로저로 초기화되어 색값을 설정한다.
struct Chessboard {
let boardColors: [Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareIsBlackAt(row: Int, column: Int) -> Bool {
return boardColors[(row * 8) + column]
}
}
Whenever a new Chessboard instance is created, the closure is executed, and the default value of boardColors is calculated and returned. The closure in the example above calculates and sets the appropriate color for each square on the board in a temporary array called temporaryBoard, and returns this temporary array as the closure’s return value once its setup is complete. The returned array value is stored in boardColors and can be queried with the squareIsBlackAt(row:column:) utility function:
새로운 Chessboard 인스턴스가 생성될때마다, 클로저가 실행되고, boardColors 의 기본값이 계산되어 리턴된다.
위 예제의 클로저는 temporaryBoard라는 임시배열안에서 판위의 각 사각형에 대하여 적절한 색을 계산하고 설정하고,
설정이 완료되면 이 임시 배열을 리턴한다.
리턴된 배열값은 boardColors 에 저장되고 squareIsBlackAt(row:column:) 함수로 조회될수 있다.
let board = Chessboard()
print(board.squareIsBlackAt(row: 0, column: 1))
// Prints "true"
print(board.squareIsBlackAt(row: 7, column: 7))
// Prints "false"
'iOS 초보' 카테고리의 다른 글
[swift5.1번역] 16.Optional Chaining (0) | 2019.10.11 |
---|---|
[swift5.1번역] 15.Deinitialization (0) | 2019.09.17 |
[swift5.1번역] 13.Inheritance (0) | 2019.08.27 |
[swift5.1번역] 12.Subscripts (0) | 2019.08.21 |
[swift5.1번역] 11.Methods (0) | 2019.08.19 |