이번에 다룰 주제는 초기화!
Initialization is the process of preparing an instance of a class, structure, or enumeration for use.
초기화는 클래스, 구조체, 열거형의 인스턴스를 준비하는 과정이라고 하는데
An instance of a class is traditionally known as an object. However, Swift structures and classes are much closer in functionality than in other languages, and much of this chapter describes functionality that applies to instances of either a class or a structure type. Because of this, the more general term instance is used.
대충 다른 언어의 객체(Object)와 비슷한 개념이다.
그런데 이제 구조체를 곁들인.
특정 타입의 새 인스턴스를 만들기 위해서 호출할 수 있는 특수한 메서드같은 것이 이니셜라이저.
그것이 바로 디이니셜라이저이다.
클래스에서만 사용할 수 있는데, 자세한 것은 언젠가 디이니셜라이저를 다루게 된다면 설명해보겠다.
이 부분에 대해 작성하기 위해 저장 프로퍼티에 대한 글을 쓴 것이나 다름 없다.
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 can’t be left in an indeterminate state.
저장 프로퍼티는 초기화가 완료되는 시점까지 꼭 초기 값이 설정되어야 한다.
프로퍼티 기본 값이나 이니셜라이저에서 설정한 초기 값은 프로퍼티 옵저버를 호출하지 않고 값이 설정된다.
프로퍼티에 새 값이 설정될 때마다 호출되는 녀석인데, 이 부분은 아마 나중에 다루게 되지 않을까?
가장 간단한 형태는 init
키워드만 사용해 작성된 매개변수가 없는 메서드와 같은 형태.
init() {
// 여기에서 초기화를 수행한다.
}
매개변수 없이 설정하므로 내부에서 초기화를 수행해주면 된다.
struct Gundam {
var pilot: String
init() {
pilot = "아무로 레이"
}
}
역시 건담은 아무로가 타야지.
프로퍼티를 정의할 때 기본 값을 할당할 수도 있다는 것은 저장 프로퍼티에 대한 글에서 설명했다.
If a property always takes the same initial value, provide a default value rather than setting a value within an initializer.
항상 같은 값을 사용한다면 이니셜라이저에서 설정하지 말고 그냥 프로퍼티 기본 값을 주라고 한다.
struct Gundam {
var pilot: String = "아무로 레이"
}
그렇다면 아까의 코드를 이렇게도 바꿀 수 있겠다.
아까와 코드는 다르지만 Gundam.init()
혹은 Gundam()
을 호출할 때의 결과는 같다.
그런데 요즘 시대는 뭐든 DIY가 짱이지 않은가?
밋밋한 init()
은 조금 심심하지 않은가? 이렇게 저렇게 만들어보자.
함수나 메서드를 작성해봤다면 매개변수(parameter)란 개념을 알 것 같은데, 이니셜라이저의 매개변수는 함수의 그것과 정확히 같다.
struct Plamodel {
var height: Int
init(fromHG highGrade: Int) {
height = highGrade * 144
}
init(fromMG masterGrade: Int) {
height = masterGrade * 100
}
}
첫 번째로 전달인자 레이블이 fromHG
고 매개변수 이름이 highGrade
인 이니셜라이저가 있다.
HG는 1/144 스케일이라 144를 곱해준다.
두 번째 이니셜라이저는 전달인자 레이블이 fromMG
, 매개변수 이름이 masterGrade
이다.
마찬가지로 MG는 1/100 스케일이라 100을 곱한다.
만약 그렇지 않으면?
struct Person {
let name: String
init(name: String) {
self.name = name
}
}
그냥 매개변수 이름만 작성할 수도 있다.
self
키워드를 통해 구분해서 사용해야 한다.간단히 말하면 이니셜라이저 내부에서는 매개변수의 이름이 우선도가 높기 때문. 이는 함수나 메서드를 작성할 때도 마찬가지다.
그런데 이니셜라이저는 함수나 메서드와 달리 이름이 없으니까 구분할 방법은 이 매개변수 뿐이기 때문에 만약 같은 이름에 같은 타입의 매개변수 목록을 갖는 이니셜라이저를 두 개 만들려고 하면 문제가 된다.
let goku = Person(name: "손오공")
let android18 = Person(name: 18)
두 선언 모두 유효하다. Swift가 타입에 깐깐하기 때문에 알잘딱하게 이니셜라이저를 골라쓰는 것.
이제 전달인자 레이블이나 매개변수 이름을 쓰는 법은 자알 알았다.
그런데 전달인자 레이블을 생략하고 싶으면?
If you don’t 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.
바로 밑줄을 긋는 것이다!
struct Person {
var name: String
init(_ name: String) {
self.name = name
}
}
전달인자 레이블 자리에 밑줄을 배치하는 것만으로도 전달인자 레이블을 생략할 수 있다.
써 보면 안다.
let luffy = Person("몽키 D 루피")
Person(name: "몽키 D 루피")
나 Person(_: "몽키 D 루피")
가 나오는 것이 적절할 것 같지만 아예 전달인자 레이블부터 콜론까지를 생략할 수 있는 방법이다.
그러니까 밑줄을 기억해두시길.
옵셔널 타입인 저장 프로퍼티의 경우 초기화 시점에 논리적으로 값이 없을 수 있다.
혹시 옵셔널이 무엇인지 궁금하다면 이 글을 보고오시길.
그렇다면 옵셔널 프로퍼티는 초기화할 때 어떻게 되는가?
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.
앞서 말했듯이 저장 프로퍼티는 초기화가 완료되는 시점까지 초기 값이 있어야 하므로 우선 nil
로 할당된다.
다만 초기화가 끝나도 아직 값이 없을 뿐이다.
struct Man {
var name: String
var girlFriend: String?
init(name: String) {
self.name = name
}
}
let gundy = Man(name: "건디")
gundy.girlFriend // nil. 아직 없음. 그저 그뿐.
주의할 점이 하나 있다.
옵셔널 프로퍼티를 let
, 즉 상수로 선언하면 초기화 시 마음대로 nil
로 설정할 수 없으므로 초기 값을 설정하라는 에러가 뜬다.
그러니까 인스턴스 생성 이후 언젠가 값을 할당할 것이라면 var
를 사용하자!
그런데 또 주의할 점이 있다.
You can assign a value to a constant property at any point during initialization, as long as it’s set to a definite value by the time initialization finishes. Once a constant property is assigned a value, it can’t be further modified.
언제 설정하든 상관 없지만 상수일 경우 한 번 설정하면 바꿀 수 없다는 것이다.
하루종일도 바꿀 수 있다.
Swift provides a default initializer for any structure or class that provides default values for all of its properties and doesn’t provide at least one initializer itself. The default initializer simply creates a new instance with all of its properties set to their default values.
모든 저장 프로퍼티에 기본 값이 있고, 따로 만들어 둔 이니셜라이저가 전혀 없을 때 기본 이니셜라이저가 자동으로 만들어진다.
기본 이니셜라이저라는 이름에 걸맞게 모든 프로퍼티가 프로퍼티 기본 값으로 설정된다.
struct Gundam {
var pilot: String = "아무로 레이"
}
바로 이 코드다.
그렇다면 이 상황에서 인스턴스를 만들면?
let firstGundam = Gundam()
print(firstGundam.pilot) // "아무로 레이" 출력
그건 이제 기본 이니셜라이저와는 다르다.
struct Animal {
var name: String
var numberOfLegs: Int = 4
}
이런 타입이 있다면 지금 이니셜라이저를 따로 만들지 않았으니까 Animal
타입은 init(name: String, numberOfLegs: Int)
라는 이니셜라이저가 자동 생성된다.
모든 저장 프로퍼티의 이름이 매개변수 이름으로 자동 생성된다.
그런데 numberOfLegs
에는 프로퍼티 기본 값이 있으므로 그를 제외한 init(name: String)
이니셜라이저도 호출할 수 있다.
let monkey = Animal(name: "원숭이", numberOfLegs: 2)
let lion = Animal(name: "사자")
두 이니셜라이저는 같은 이니셜라이저인가?
자동으로 생성된 멤버와이즈 이니셜라이저의 정의가 어떻게 되어있는지 확인해보면 다음과 같이 나온다.
즉 매개변수 기본 값이 있는 이니셜라이저가 하나 생성되는 것.
아까의 Gundam
타입을 통해 확인해보면 맞다.
하지만 똑같이 프로퍼티 기본 값이 있더라도 클래스의 경우는 매개변수 기본 값이 있는 이니셜라이저가 아니라 매개변수가 아예 없는 이니셜라이저로 기본 이니셜라이저가 생성된다.
클래스 기본 이니셜라이저 | 구조체 기본 이니셜라이저 | 멤버와이즈 이니셜라이저 | |
---|---|---|---|
프로퍼티 기본 값 필요 | ⭕️ | ⭕️ | ❌ |
자동으로 생성 | ⭕️ | ⭕️ | ⭕️ |
커스텀 이니셜라이저 존재 | ❌ | ❌ | ❌ |
오늘은 저장 프로퍼티의 초기화와 관련된 초기화 과정에 대해 살펴보았다.
nil
이 할당된다.