초기화는 클래스, 구조체, 열거형 인스턴스를 사용하기 위해 준비 작업을 하는 단계이다. 이 단계에서 각 저장 프로퍼티의 초기값을 설정한다. 초기화 과정은 Initializer를 정의하는 것으로 구현할 수 있다. Swift의 Initializer는 값을 반환하지 않는다. 초기화와 반대로 여러 값과 자원의 해지를 위해 Deinitializer도 사용할 수 있다.
인스턴스의 저장 프로퍼티는 사용하기 전에 반드시 특정값으로 초기화 돼야 한다. 이 값으로 기본값을 설정할 수 있고, 특정값을 설정할 수도 있다.
Initializer에서 저장 프로퍼티에 값을 직접 설정하면 프로퍼티 옵저버가 호출되지 않고 값 할당이 수행된다.
Initializer는 특정 타입의 인스턴스를 생성한다. Initializer의 가장 간단한 형태는 파라미터가 없고 init 키워드를 사용하는 것이다.
init() {
// perform some initialization here
}
다음 예제는 화씨 온도 구조체를 만들어 온도라는 프로퍼티를 선언하고 Initializer에 초기화하는 코드이다.
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// "The default temperature is 32.0° Fahrenheit" 출력
프로퍼티의 선언과 동시에 값을 할당하면 그 값을 초기값으로 사용할 수 있다.
struct Fahrenheit {
var temperature = 32.0
}
위 코드처럼 프로퍼티를 선언과 동시에 초기값을 할당할 수 있다.
또한 프로퍼티에 타입을 선언하지 않아도 컴파일러는 초기값을 참조해서 타입을 추론할 수 있다. 이 기본값은 상속시 함께 상속된다.
초기화 프로세스를 입력값과 옵셔널 프로퍼티 타입 혹은 상수값을 할당해서 커스터마이징 할 수 있다.
초기화 정의에 파라미터를 정의해 사용할 수 있다. 다음 코드는 temperatureInCelsius 프로퍼티를 초기화 파라미터로 입력받아 초기화에 사용하는 코드이다.
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
메소드 파라미터와 초기화 파라미터 모두 파라미터 이름과 인자 레이블을 갖지만, Initializer는 특정 메소드에서 지정하는 메소드 이름을 지정하지 않고 Initializer 식별자로 파라미터를 사용한다. 모든 파라미터는 인자 레이블을 갖는데 만약 사용자가 이 레이블을 지정하지 않은면 Swift가 자동으로 하나를 할당해 제공한다.
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
}
}
위에 정의한데로 Color 인스턴스를 인자값 3개 혹은 하나를 이용해 생성할 수 있다.
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
아래 코드는 초기화 과정에 인자 레이블이 포함돼 있지 않아서 컴파일 에러가 발생한다.
let veryGreen = Color(0.0, 1.0, 0.0)
// ERROR!
코드를 작성할 때 인자 레이블을 생략하는 것이 명료한 경우 _ 기호를 사용해 Initializer에서 인자 레이블을 생략할 수 있다.
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
let bodyTemperature = Celsius(37.0)
코드와 같이 레이블 없이 Celsius 인스턴스를 초기화할 수 있다.
프로퍼티의 최초값이 없고 나중에 추가될 수 있는 값을 옵셔널로 선언해 사용할 수 있다. 옵셔널 프로퍼티는 자동으로 nil로 초기화 된다.
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."
Initializer에서는 상수 프로퍼티에 값을 할당하는 것도 가능하다.
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.)"
만약 모든 프로퍼티의 초기값이 설정돼 있고, 하나의 초기자도 정의하지 않았다면 Swift는 모든 프로퍼티를 기본값으로 초기화하는 기본 초기자를 제공해준다.
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
위 코드는 Initializer를 정의하지 않았지만 Swift가 제공하는 기본 Initializer ShoppingListItem()
를 사용할 수 있음을 보여주는 예제이다.
기본 Initializer와 다르게 멤버쪽 Initializer는 프로퍼티가 기본값이 없어도 커스텀 Initializer를 정의하지 않았다면 멤버쪽 Initializer를 제공해 준다.
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
Initializer에서 다른 Initializer를 호출할 수 있다. 이 과정을 Initializer 위임이라 한다.
값 타입과 클래스 타입간 Initializer 위임 동작이 다르다. 값 타입은 상속을 지원하지 않아 Initializer를 자기 자신의 다른 Initializer에만 사용 할 수 있다. 반면 클래스 타입은 상속이 가능하기 때문에 superclass의 Initializer를 subclass에서 호출 가능하다. 상속 받은 모든 프로퍼티의 초기화는 책임이 Initializer에 있다. 커스텀 Initializer 선언은 기본 Initializer 혹은 멤버쪽 Initializer를 사용할 수 없다.
이런 제약이 Initializer의 복잡성을 낮춰주고 Initializer가 의도하지 않게 사용되는 것을 방지해 준다.
다음은 Size와 Point라는 값 타입 구조체를 선언한 코드이다.
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
위에서 정의한 구조체를 다른 구조체에서 프로퍼티로 사용한다.
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)
}
}
이 구조체를 초기화 하는 방법은 세 가지가 있다.
첫 Initializer, init()
를 이용해 초기화를 하면 아래와 같이 각 프로퍼티가 기본 값을 초기 값으로 갖게 된다.
let basicRect = Rect()
// basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)
두번째 Initializer, init(origin: Point, size: Size)
를 사용하면 초기화를 수행할 때, 프로퍼티의 값을 지정할 수 있다. 이 Initializer는 멤버쪽 Initializer와 동일하다.
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)
마지막 Initializer, init(center: Point, size: Size)
에서는 내부에서 다른 초기자인 init(center: Point, size: Size)
를 사용한다. 그래서 특정 수행을 한 후 init(center: Point, size: Size)
를 호출해 초기화를 이 Initializer에게 위임한다.
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)
모든 클래스의 저장 프로퍼티와 superclass로부터 상속받은 모든 프로퍼티는 초기화 단계에서 반드시 초기값을 할당 받아야 한다. Swift에서는 클래스 타입에서 모든 프로퍼티가 초기값을 갖는 것을 보장하기 위해 2가지 방법을 지원한다.
지정 초기자는 클래스의 주 초기자이다. 지정 초기자는 클래스의 모든 프로퍼티를 초기화한다. 클래스 타입은 반드시 한개 이상의 지정 초기자가 있어야 한다.
편리 초기자는 초기화 단계에서 미리 지정된 값을 사용해서 최소한의 입력을 초기화를 할 수 있도록 해주는 초기자이다. 편리 초기자 내에서 반드시 지정 초기자가 호출돼야 한다.
클래스의 지정 초기자의 문법은 값 타입의 초기자와 같다.
init(parameters) {
Statements
}
편리 초기자는 기본 초기자와 문법이 같지만 init 앞에 convenience 키워드를 붙여준다.
convenience init(parameters) {
statements
}
지정 초기자와 편리 초기자 사이의 관계를 단순하게 하기 위해 Swift는 Initializer를 위임하는 호출을 위해 다음 규칙을 따른다.
지정 초기자는 반드시 직계 superclass의 지정 초기자를 호출한다.
편리 초기자는 반드시 같은 클래스의 다른 초기자를 호출한다.
편리 초기자는 궁극적으로 지정 초기자를 호출한다.
요약하자면 지정 초기자는 반드시 위임을 superclass로 해야하고 편리 초기자는 반드시 위임을 같은 레벨에서 해야한다.
Subclass의 편리 초기자는 같은 레벨에서 다른 지정 초기자를 호출하고 지정 초기자는 초기화를 상위 클래스에게 위임해 상위 클래스의 지정 초기자가 호출되는 것을 확인할 수 있다.
아래는 조금 더 복잡한 초기화 위임의 형태이다.
Swift에서 클래스 초기화는 2단계로 진행된다. 첫번째 단계에서는 각 저장된 프로퍼티는 초기값으로 초기화 된다. 모든 저장된 프로퍼티의 상태가 결정되면 두번째 단계가 시작된다. 두번째 단계에서는 새로운 인스턴스의 사용이 준비됐다고 알려주기 전에 저장된 프로퍼티를 커스터마이징하는 단계이다.
Swift의 컴파일러는 2단계 초기화가 에러없이 끝나는 것을 보장하기 위해 4단계 안전 확인을 한다.
안전 확인 1단계
지정 초기자는 클래스 안에서 초기화를 superclass의 초기자에게 위임하기 전에 모든 프로퍼티를 초기화 해야 한다. 위에서 언급한 것 처럼 메모리에서 객체는 모든 저장된 프로퍼티가 초기 상태를 갖어야만 완전히 초기화 된 것으로 간주되기 때문에 이 규칙을 만족시키기 위해 지정 초기자는 반드시 다른 초기자로 넘기기 전에 소유하고 있는 모든 프로퍼티를 초기화 해야 한다.
안전 확인 2단계
지정 초기자는 반드시 상속된 값을 할당하기 전에 superclass의 초기자로 위임을 넘겨야한다. 그렇지 않으면 상속된 값이 superclass의 초기자에 의해 덮어 쓰여지게 된다.
안전 확인 3단계
편리 초기자는 반드시 어떤 프로퍼티를 할당하기 전에 다른 초기자로 위임을 넘겨야 한다. 만약 그렇지 않으면 편리 초기자에 의해 할당된 값을 다른 클래스의 지정 초기자에 의해 덮어 쓰여지게 된다.
안전 확인 4단계
Initializer는 초기화의 1단계가 끝나기 전에는 self의 값을 참조하거나 어떤 인스턴스의 프로퍼티, 메소드 등을 호출하거나 읽을 수 없다.
Swift에서는 기본적으로 subclass에서 superclass의 Initializer를 상속하지 않는다. 이유는 superclass의 Initializer가 무분별하게 상속되면 복잡하게 되어 subclass에서 잘못 초기화 되는 것을 막기 위함이다.
만약 클래스에서 모든 프로퍼티의 초기값이 지정 되어 있고 아무런 커스텀 초기자를 선언하지 않았다면 기본 초기자 init()을 사용할 수 있다.
superclass의 초기자를 오버라이드 하기 위해서 subclass에서 그 초기자에 override 키워드를 붙이고 재정의한다.
다음은 클래스를 생성하고 그것의 subclass에서 초기자를 오버라이드해 사용하는 예제이다.
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
인스턴스 생성 후 초기값을 확인한다.
let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")
// "Vehicle: 0 wheel(s)" 출력
subclass에서 superclass의 초기자를 오버라이드 한다.
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
인스턴스 생성 후 초기값이 변한 것을 확인할 수 있다.
let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")
// "Bicycle: 2 wheel(s)" 출력
subclass의 초기자에서 var은 변경 가능하지만 let은 변경할 수 없다.
앞서 언급했던 것과 같이 Subclass는 Superclass의 초기자를 기본적으로 상속하지 않는다. 하지만 특정 상황에서 자동으로 상속 받는다. 사실 많은 상황에서 직접 초기자를 오버라이드 할 필요가 없다. Subclass에서 새로 추가한 모든 프로퍼티에 기본값을 제공하면 다음 두가지 규칙이 적용된다.
Subclass가 지정초기자를 정의하지 않으면 자동으로 Superclass의 모든 지정초기자를 상속한다.
Subclass가 Superclass의 지정초기자를 모두 구현한 경우 자동으로 Superclass의 편리 초기자를 추가한다.
다음은 지정초기자, 편리한 초기자 그리고 자동 초기자의 상속의 예제 이다.
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
편리 초기자 convenience init()
에서 지정초기자 init(name: String)
이 호출되는 형태이다.
Food
클래스는 모든 프로퍼티의 기본값이 있는게 아니므로 멤버쪽 초기자를 갖지 않는다.
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"
위 예제는 지정 초기자를 이용해 Food
인스턴스의 name
의 초기값을 “Bacon”으로 설정해 생성하는 예제다. 다음 예제는 편리한 초기자를 이용해 인스턴스를 생성한 코드이다.
let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]"
mysteryMeat
은 초기값을 지정하지 않았음으로 편리한 초기자에 의해 "[Unnamed]”를 갖는다.
다음 코드는 Food
클래스를 서브클래싱해서 생성한 RecipeIngredient
클래스에서 Superclass의 편리 초기자를 override
해서 사용한 예제다.
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)
}
}
Superclass의 init(name: name)
초기자를 상속받아 지정 초기자를 생성하고 그 지정 초기자를 편리 초기자 convenience init(name: String)
에서 override
를 사용한다. RecipeIngredient
에서 초기자가 사용되는 구조를 표현하면 다음 그림과 같다.
이렇게 생성한 RecipeIngredient
클래스는 다음 3가지 형태의 초기자를 이용해 인스턴스를 생성할 수 있다.
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
마지막으로 RecipeIngredient
를 상속받아 생성한 ShoppingListItem
클래스에서의 초기자에 대해 살펴보겠습니다. ShoppingListItem
는 purchased
라는 boolen 타입 프로퍼티와 description
이라는 계산된 프로퍼티 2가지를 새로 생성한다.
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
}
이제 이 모든 3개의 초기자를 이용해 ShoppingListItem
인스턴스를 생성할 수 있다.
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 ✘
초기화 과정 중에 실패할 가능성이 있는 초기자를 init뒤에 물음표(?)를 사용해 실패 가능 초기자라고 표시할 수 있다.
let wholeNumber: Double = 12345.0
let pi = 3.14159
if let valueMaintained = Int(exactly: wholeNumber) {
print("\(wholeNumber) conversion to Int maintains value of \(valueMaintained)")
}
// "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")
}
// "3.14159 conversion to Int does not maintain value" 출력
다음 코드는 초기자에 입력값이 없으면 초기화 실패가 발생하도록 구현한 예제다.
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
위에서 만든 Animal
구조체를 이용해 인스턴스를 생성하는데 동물의 종류를 Giraffe
로 지정해 생성한 예제다.
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)")
}
// "An animal was initialized with a species of Giraffe" 출력
다음은 Animal
생성시 인자를 넣지 않고 생성한 경우 초기화에 실패해서 생성된 인스턴스는 nil을 갖게 되는 것을 확인할 수 있다.
let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?, not Animal
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// "The anonymous creature could not be initialized" 출력
열거형에서도 실패 가능 초기자를 사용할 수 있다.
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
}
}
}
위 예제에서는 초기화시 지정된 특정 온도 표시 단위가 아닌 경우 초기화 결과로 nil을 반환한다.
TemperatureUnit(symbol: "F")
에서 F는 TemperatureUnit
열거형에서 사전에 정의돼 있는 단위여서 초기화가 정상적으로 수행됩니다. 반면 TemperatureUnit(symbol: "X")
에서 X는 정의되지 않은 단위여서 초기화에 실패합니다.
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// "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.")
}
// "This is not a defined temperature unit, so initialization failed." 출력
열거형의 각 케이스에 지정돼 있는 Raw값을 초기자 인자로 넣어 초기화에 사용할 수 있다.
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.")
}
// "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.")
}
// "This is not a defined temperature unit, so initialization failed." 출력
동작은 앞선 TemperatureUnit
과 같지만 TemperatureUnit
에서 초기자 구현이 훨씬 간단해졌다.
실패 가능 초기자에서 실패가 발생하면 즉시 관련된 초기자가 중단 된다.
아래 코드는 Product
의 name
이 없거나 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 }
self.quantity = quantity
super.init(name: name)
}
}
아래 코드는 name
도 있고 quantity
도 1 이상이므로 인스턴스 생성에 성공한다.
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// "Item: sock, quantity: 2" 출력
아래 코드는 인스턴스 생성시 quantity
가 0이므로 초기화에 실패한다.
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
}
// "Unable to initialize zero shirts" 출력
아래 코드는 quantity
는 1이상이지만 name
이 비어있기 때문에 초기화에 실패한다.
if let oneUnnamed = CartItem(name: "", quantity: 1) {
print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
// "Unable to initialize one unnamed product" 출력
Superclass의 실패 가능 초기자를 Subclass에서 실패 불가능한 초기자로 Overriding 할 수 있다.
아래 Document 클래스는 초기화를 할 때, name
값으로 특정 String 지정하거나 nil을 지정할 수 있다. 하지만 name
값이 비어 있는 경우에는 초기화 실패를 나타내는 nil을 반환한다.
class Document {
var name: String?
init() {}
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
Document를 서브클래싱한 AutomaticallyNamedDocument
클래스에서는 기본 초기자와 지정 초기자를 오버라이딩해서 실패 가능 초기자를 실패 불가능한 초기자로 만들었습니다. 초기값이 없는 경우에는 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
}
}
}
Document를 서브클래싱한 또다른 클래스 UntitledDocument에서는 기본 초기자를 오버라이딩해서 초기자를 구현했고 그 값이 옵셔널 값을 갖지 않도록 느낌표(!)를 사용해 강제로 언래핑했다.
class UntitledDocument: Document {
override init() {
super.init(name: "[Untitled]")!
}
}
실패 가능 초기자 init? 을 init! 으로 오버라이딩 할 수 있고 아니면 위임해서 사용할 수 있다.
모든 Subclass에서 반드시 구현해야 하는 초기자에는 아래 예제와 같이 required
키워드를 붙여준다.
class SomeClass {
required init() {
// initializer implementation goes here
}
}
필수 초기자를 상속받은 Subclass에서도 반드시 required
키워드를 붙여서 다른 Subclass에게도 이 초기자는 필수 초기자라는 것을 알려야 한다.
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
기본값 설정이 단순히 값을 할당하는 것이 아니라 다소 복잡한 계산을 필요하다면 클로저나 함수를 이용해 값을 초기화 하는데 이용할 수 있다. 기본값을 지정하기 위해 클로저를 사용하는 형태의 코드는 다음과 같다.
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
}()
}
someProperty
는 클로저가 실행된 후 반환 타입이 SomeType
인 SomeValue
를 기본값으로 갖게 된다.
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]
}
}
Chessboard
에서 boardColors
는 클로저를 이용해 8x8의 보드 색을 갖는 Bool 배열로 초기화 된다. 보드의 특정 행-열이 어떤 색인지 확인하는 함수 squareIsBlackAt(row: Int, column: Int)
가 제공된다.
let board = Chessboard()
print(board.squareIsBlackAt(row: 0, column: 1))
// Prints "true"
print(board.squareIsBlackAt(row: 7, column: 7))
// Prints "false"