애플 스위프트 언어 개발 가이드를 기반으로 초기화 관련된 내용을 정리했습니다. 초기화가 복잡합니다. 크게 복잡하지 않을 수도 있는 내용이지만 언어마다 조금씩 다른 부분을 잘 정리했습니다.
http://cafe.naver.com/architect1 에서 스터디 진행중입니다.
기본적인 뼈대는 http://swift.leantra.kr/ 를 기반으로 합니다.
5. 사용자 정의 초기화 - 초기화 파라메터
struct Celsius {
var temperatureInCelsius: Double = 0.0
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (faherenheit - 32.0)/1.8
}
}
6. 사용자 정의 초기화 - 지역 파라메터
struct Color {
let red = 0.0, green = 0.0, blue = 0.0
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
}
7. 사용자 정의 초기화 - 외부 파라메터
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let veryGreen = Color(0.0, 1.0, 0.0)
init 의 파라메터는 기본적으로
지역 파라메터 + 외부 파라메터 이다.
9. class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
println(text)
}
}
옵셔널 속성 타입
let cheeseQuestion =
SurveyQuestion(text: “Do you like
cheese?”)
cheeseQuestion.ask()
cheeseQuestion.response = “Yes, I do
like cheese.”
10. class SurveyQuestion {
let text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
println(text)
}
}
초기화 과정중에 상수 속성을 변경하기
let cheeseQuestion =
SurveyQuestion(text: “How about
beets??”)
cheeseQuestion.ask()
cheeseQuestion.response = “I also
like beets.”
11. 기본 초기자
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
모든 속성은 기본 값을 가져야 합니다.
옵셔널은 자동 기본값으로 nil을 받습
니다.
12. 구조체 타입의 멤버 단위 초기화
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0,
height: 2.0)
모든 속성에 기본값이 있고,
사용자 정의 초기자가 없으면
=> 구조체 타입은 자동으로 멤버 단위
초기자를 가진다.
13. 값 타입 = 구조체, 열거자
값 타입의 초기자 대리자
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
14. 값 타입의 초기자 대리자
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)
}
}
정사각형을 그리는 방법은 3가지가 있다.
1. (0, 0)에 위치하는 크기 0 짜리 정사각형
2. (x, y)에 위치하는 크기 n 짜리 정사각형
3. (x, y)를 중심으로하는 크기 n짜리 정사각
형
이 초기화 시나리오이다. 3번 째 초기자에
서, 2번째 초기자를 호출 또는 대리 한다.
16. 클래스 상속과 초기화
지정 초기자
주 초기자
모든 속성을 완전히 초기화
+
적절한 부모 클래스 초기자 호출
모든 클래스는 반드시 하나 이상의 지정 초기자를
가져야 한다.
편의 초기자
부 초기자
같은 클래스내의 지정 초기자를 호출하는 초기자
호출하는 지정 초기자의 몇몇 파라메터를 기본으
로 하는 초기자
특정 용도나 입력 값 타입에 대한 클래스 인스턴스
를 만드는 초기자
18. 1 Phase
해당 클래스가 가지는 저장속성에 초기값을 할당 한다.
2 Phase
클래스 인스턴스를 사용할 준비가 되기 전까지 속성 값을 변경한다.
두 단계 초기화
19. 안전 점검 1
부모 클래스의 초기자를 위임하기 전에, 내 클래
스의 속성이 초기화 되었는지 확인한다.
안전 점검 2
지정 초기자는 상속받은 속성에 값을 할당하기 전
에 부모 클래스의 초기자를 수행해야 합니다. (값
이 부모 클래스의 초기화 중에 덮어씌워진다.)
안전 점검 3
편의 초기자는 속성에 값을 할당하기전에 다른 초
기자를 대리 수행 해야 합니다. 그렇지 않으면 편
의 초기자가 할당한 값은 지정 초기자에 의해 덮어
씌워집니다.
안전 점검 4
초기자는 인스턴스 메소드를 호출 할 수 없습니다.
초기화 첫 단계가 끝나기 전에는 self도 참조 할 수
없습니다.
두 단계 초기화
22. swift의 자식 클래스는 기본적으로 부모 클래스의 초기자를 상속 받지 않습니다
.
(=! object-C)
초기자 상속과 오버라이딩
23. 초기화 중간에 뭔가 수정 하기 위해서, 부모 클래스와 같은 초기자를 가진 자식
클래스가 필요 할 때, 자식 클래스에 같은 초기자를 오버라이딩 해서 구현 할 수
있습니다.
오버라이딩 하는 초기자가 지정 초기자라면, 오자식 클래스에서 구현체를 오버
라이드 하고, 오버라이딩 하는 버전에서 부모 버전의 초기자를 호출 하도록 할
수 있습니다.
초기자 상속과 오버라이딩
24. Rule 1
자식 클래스가 지정 초기자를 정의하지 않는다.
Rule 2
자식 클래스가 부모 클래스의 모든 지정 초기자를 구현한다.
초기자 자동 상속
25. 편의 초기자는 convenience를 사용합
니다.
convenience init(parameters) {
statements
}
클래스의 지정 초기자는 값 타입을 위
한 단순 초기자와 같은 방식으로 작성
한다.
init(parameters) {
statements
}
지정 초기자, 편의 초기자의 문법
26. class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: “[Unnamed]”)
}
}
지정 초기자와 편의 초기자 실습
27. class RecipeIngredient: Food {
var quantity: Int
init(name:String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
지정 초기자와 편의 초기자 실습
32. class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = “(quantity) x
(name.lowercaseString)”
output += purchased ? “V” : “X”
return output
}
}
지정 초기자와 편의 초기자 실습
33. class SomeClass {
let someProperty: SomeType = {
return someValue}()
}
클로저나 함수로 기본 속성 값을 설정하
기
#8:지역 파라메터는 init 안에서만 쓸 수 있는 파라메터이다.
외부 파라메터는 외부에서 init을 호출 할 때 반드시 이름을 magenta 를 초기화 하는 것 처럼 명시해야 한다. 그래서 veryGreen은 오류이다.
#9:옵셔널은 그냥 int 가 아니라 int? / optional int 타입이다. toInt() 했을때 possibleNumber가 nil일 수 있다. 이 때 !를 통해서 강제 언래핑 하면 fatal exception이 발생 한다.
#11:클래스 인스턴스는 상수 속성의 값을 오직 초기화 과정중에 해당 클래스에 의해서만 바꿀 수 있습니다. 상수 속성은 자식(sub) 클래스에 의해 변경될 수 없습니다
#12:반드시 클래스의 모든 속성이 기본 값을 가지고 있어야 합니다. 그래야 자동으로 기본 초기자를 구현할 수 있습니다.
단, 옵셔널은 자동으로 기본값으로 nil을 받습니다.
#13:구조체 타입은 자동으로 멤버 단위 초기자를 가진다.
단, 모든 저장 속성에 기본 값이 제공되고, 사용자 정의 초기자가 없어야 한다.
#14:값 타입은 상속을 허용하지 않기 때문에 초기자를 대리하는게 상대적으로 쉽다. 왜냐하면 자기 자기가 가지고 있는 다른 초기자만 대리 할 수 있기 때문이다.
#18:초기자 연쇄
지정초기자는 반드시 바로 위 부모 클래스의 지정 초기자를 호출한다.
편의 초기자는 반드시 같은 클래스 내의 초기자를 호출한다.
편의 초기자는 반드시 지정 초기자를 호출하는 것으로 끝내야 한다.
#19: 이렇게 두 단계 초기화를 함으로써, 초기화를 안전하게 하고, 상속 계층 상에서 유연성을 가진다. 속성 값이 초기화 되기 전에 접근하는 것을 반지, 다른 초기자 값을 설정하는 것을 방지 한다.
스위프트의 이 단계 초기화 과정은 오브젝티브 C의 초기화와 비슷합니다. 주요한 차이점은 첫번째 단계에 있습니다. 오브젝티브 C는 0이나 널(null) 값(0 또는 nil)을 모든 속성에 할당합니다. 스위프트의 초기화 흐름은 좀 더 유연하려 사용자 정의 초기값을 설정할 수 있게 해줍니다. 그리고 0이나nil이 기본값으로 유효하지 않은 타입에 대처할 수 있게 합니다.
#21:- 클래스의 지정 초기자 또는 편의 초기자 호출
- 클래스 인스턴스를 위한 메모리 할당
- 클래스 지정 초기자가 해당 클래스에있는 모든 저장속성이 값이 있는지 확인. 해당 저장 속성을 위한 메모리 초기화 완료.
- 지정 초기자는 부모 클래스 지정 초기자로 작업을 넘겨줍니다.
- 상속 계층의 맨 꼭대기까지 계속 됩니다.
- 연쇄의 꼭대기에서 마지막 클래스는 모든 저장속성이 값을 가졌는지 확인하고, 인스턴스의 메모리는 완전히 초기화 되었습니다. 첫 단계를 종료합니다.
#22:연쇄의 꼭대기에서 거꾸로 내려오면서 각각의 지정 초기자는 해당 클래스 인스턴스를 수정 할 수 있습니다. 초기자들은 이제 self에 접근하고, 프로퍼티를 수정 할 수 있습니다. 인스턴스 메소드도 호출 할 수 있습니다.
- 끝으로, 모든 편의 초기자들은 해당 클래스 인스턴스를 수정 할 수 있고, self를 사용 할 수 있습니다.
#23: 이런 접근 방식은 더 복잡한 자식 클래스가 부모 클래스의 단순한 초기자를 자동으로 상속받아서 불완전하게 초기화되는 것을 막아줍니다.
#24: 아마도 초기화 도중에 클래스의 무엇인가를 수정 하고 싶을 때, 수정한 자식 클래스가 부모로 부터 초기자를 상속 받기를 원할 것 입니다. 이 때는 수정한 자식 클래스에 같은 초기자를 오버라이딩해서 구현 할 수 있습니다.
지정 초기자를 오버라이딩 한다면, 자식 클래스에서 구현체를 오버라이드 할 수 있고, 오버라이딩한 버전에서 부모 클래스 버전의 초기자를 호출 할 수 있습니다. 편의 초기자를 오버라이딩 하고싶으면, 오버라이드한 초기자는 반드시 자식클래스 안의 지정 초기자를 호출해야 합니다.
#25:부모클래스의 모든 편의 초기자를 자동으로 상속한다.
(Rule 1에 따라 지정 초기자를 상속 받아서 구현 하든가, 자식 클래스 정의의 일부로서 구현 할 수 있다.)
조건을 만족하면 자식 클래스가 자동으로 부모 클래스의 초기자를 상속 받습니다. 여기 진짜 말이긴데, 결국 자식 클래스가 부모 클래스의 모든 초기자를 구현해야 한다는 말이다.
#27:클래스는 기본 멤버 단위 초기자가 없습니다. 그래서 Food 클래스는 단일 인자 name을 받는 초기자를 제공합니다.
Food 클래스는 편의 초기자도 제공합니다.
#29:RecipeIngredient 클래스에는 한 개의 지정 초기자 init(name:String, quantity: Int)가 있습니다.
이 초기자는 RecipeIngredient 인스턴스의 모든 프로퍼티를 채우는데 쓰입니다.
이 초기자는 RecipeIngredient에서 새롭게 추가된 quantity에 값을 전달하면서 시작 합니다. 그리고나서, 이 초기자는 Food 클래스의 init(name:String)를 초기자를 대신 실행 합니다.
1단계는 해당 클래스가 가지는 저장속성에 초기값을 할당 한다. 입니다.
이 프로세스는 두 단계 초기화에서 1단계를 만족합니다.
#30:RecipeIngredient는 init(name:String)도 정의 하고 있다. 이 편리 초기자는 별다른 입력이 없는 모든 RecipeIngredient의 quantity를 1로 가정 했다.
이 편의 초기자는 단순히 클래스의 지정 초기자를 대리한다.
#31: RecipeIngredient의 init(name:String) 편의 초기자가 Food의 초기자 init(name:String)과 같은 파라메터를 받습니다.
RecipeIngredient가 이 초기자를 편의 초기자로 제공하지만, RecipeIngredient는 모든 부모 클래스의 지정 초기자들을 구현한것이 된다.
그러므로, RecipeIngredient는 자동으로 모든 부모 클래스의 편의 초기자들을 상속받는다.
#32:이 예제에서 RecipeIngredient의 부모클래스 Food는 하나의 편의 초기자 init()을 제공 합니다.
따라서 이 초기자는 RecipeIngredient에 상속 됩니다. init()의 상속된 버전은 Food 버전과 똑같이 기능 합니다. 대리 수행하는 초기자 init(name:String)을 Food 버전이 아니라 ReceipeIngredient 버전을 사용 한다는 것만 빼면요.
#33:이 클래스가 도입한 모든 속성의 초기값을 제공하고, 어떤 이니셜라이저도 스스로 정의하지 않기 때문에 ShoppingListItem은 자동적으로 모든 지정 이니셜라이저와 편의 이니셜라이저를 부모 클래스에서 상속받습니다.
#34:중괄호 바로 옆에 빈 괄호 한쌍이 있는 것은 클로저를 즉시 실행 하도록 한다. 이 괄호가 없으면 값이 아니라 클로저 자체를 속성에 할당 하려고 시도 하는 것이 된다.
알아야 할 점은 클로저가 호출되는 시점은 인스턴스의 나머지는 아직 초기화 되지 않은 상태이다. 따라서 self를 쓰거나 다른 메소드를 호출 할 수 없다.
#35:중괄호 바로 옆에 빈 괄호 한쌍이 있는 것은 클로저를 즉시 실행 하도록 한다. 이 괄호가 없으면 값이 아니라 클로저 자체를 속성에 할당 하려고 시도 하는 것이 된다.
알아야 할 점은 클로저가 호출되는 시점은 인스턴스의 나머지는 아직 초기화 되지 않은 상태이다. 따라서 self를 쓰거나 다른 메소드를 호출 할 수 없다.