티스토리 뷰

Swift 3.0.1 가이드에 대응하는 정리글을 작성하였습니다!!!

Initialization (1/3) 정리 최신버전 > http://wlaxhrl.tistory.com/47




Apple 제공 Swift 프로그래밍 가이드(2.2)의 Initialization 부분을 공부하며 정리한 글입니다. 개인적인 생각도 조금 들어가있습니다.



들어가며

초기화initialization는 클래스, 구조체, ENUM의 인스턴스를 사용하기 위한 준비 과정이다. 이 과정에서 새로운 인스턴스에 필요한 셋팅들을 한다. (ex: Stored 프로퍼티들에 초기값을 셋팅)

초기화를 위해 이니셜라이저initializer를 정의하자. Swift에서는 이니셜라이저가 따로 값을 반환하지 않는다. Objective-C에서는 self = [super init]; 같은 것이 가능했지만 Swift에서는 이런 식으로 못 쓴다는 말이다.

이니셜라이저의 주목적은 새로운 인스턴스가 제대로 사용할 준비가 됐는지를 보장하는 것이다.

클래스 타입의 인스턴스는 디이니셜라이저deinitializer도 정의할 수 있다. 자세한 것은 다음 장에서 살펴보자.



Setting Initial Values for Stored Properties

클래스/구조체의 인스턴스는 생성될 때 반드시 모든 Stored 프로퍼티에 값이 셋팅되어야 한다. 따라서 정의 부분에 디폴트값이 없는 Stored 프로퍼티는 반드시 이니셜라이저 안에서 초기값 셋팅이 되어야 한다. (이때 프로퍼티 옵저버는 불리지 않는다)


<Initializers & Default Property Values>

이니셜라이저를 호출하여 새로운 인스턴스를 생성할 수 있다. 이때 정의부분에서 디폴트값이 정의되지 않은 Stored 프로퍼티들은 모두 이니셜라이저 안에서 초기값 셋팅이 되어야 한다.

init() {
    // 인스턴스 메서드와 같은 모양새다
    // 이 안에서 초기작업들을 수행
}
 // (1) 이니셜라이저 안에서 초기값 셋팅
struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}

var f = Fahrenheit()
print("디폴트 온도는 \(f.temperature)도") //32.0도



// (2) 정의할 때 초기값 할당
struct Fahrenheit {
    var temperature: Double = 32.0
}

<note> 매번 초기값이 똑같은 프로퍼티라면 정의부분에서 디폴트 값을 주는 것이 더 좋다. 명확하고 짧은 이니셜라이저를 만들 수 있으며 프로퍼티의 타입을 유추하기 쉬워지기 때문이다. 또한 디폴트 이니셜라이저와 이니셜라이저 상속에도 유리해진다. (뒤에서 설명)



Customizing Initialization

초기화 과정을 커스터마이징할 수 있다. 이와 관련해서 살펴볼 것들은 다음과 같다 : 인풋 파라미터, 옵셔널 프로퍼티 타입, 초기화 과정에서 상수 프로퍼티에 값 할당하기


<초기화 파라미터Initialization Parameters>

이니셜라이저를 정의할 때, 이니셜라이저가 받을 파라미터를 다음처럼 정의할 수 있다.

struct Celsius {
    var temperatureInCelsius: Double
    
    // 파라미터를 정의하는 방법은 메서드와 동일하다
    // external name, local name, type 등
    init(fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    
    init(fromKelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
}

let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
let freezingPointOfWater = Celsius(fromKelvin: 273.15)



<내부이름과 외부이름Local and External Parameter Names & 외부이름이 없는 이니셜라이저 파라미터Initializer Parameters Without External Names>

이니셜라이저의 파라미터는 인스턴스 메서드의 파라미터와 정책이 비슷하다. external name(외부 이름)과 local name(내부 이름)을 정의할 수 있으며, external name을 정의하지 않으면 자동으로 external name = local name이 된다. external name이 필요없다면 언더바(_)를 붙이도록 한다.

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
    }
}

let magenta  = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(0.5)

// 다음은 컴파일 에러. external name이 없기 때문이다.
let veryGreen = Color(0.0, 1.0, 0.0)



<옵셔널 프로퍼티 타입Optional Property Types>

만약 초기화 과정에서 "아직 값이 없음" 상태가 될 수 있는 Stored 프로퍼티가 필요한 경우에는 옵셔널 타입의 프로퍼티를 정의하도록 하자. 이런 프로퍼티들은 초기화 과정에서 "아직 값이 없음"의 의미로써 자동으로 nil이 할당된다.

class SurveyQuestion {
    var text: String
    var response: String? // 설문조사의 답은 미리 알 수가 없다
    
    init(text: String) {
        self.text = text
        // response 프로퍼티는 자동으로 nil로 셋팅이 된다
    }
    
    func ask() {
        print(text)
    }
}

let cheeseQuestion = SurveyQuestion(text: "Are you cheese?")
// 이 시점에서 cheeseQuestion.response 는 nil
cheeseQuestion.ask()
cheeseQuestion.response = "YES"



<초기화 과정에서 상수 프로퍼티에 값 할당하기Assigning Constant Properties During Initialization>

초기화 과정에서 상수 프로퍼티에 초기값을 셋팅할 수 있다. (주: 상수 프로퍼티는 한 번 값이 할당되면 그 다음부터는 변경할 수가 없다. 따라서 이 말은 상수 프로퍼티를 정의할 때 디폴트값을 셋팅하지 않은 경우에만 해당하는 말이다.)

class SurveyQuestion {
    let text: String // 디폴트값이 없기 때문에(=아직 값이 할당되지 않아서)
    var response: String?
    
    init(text: String) {
        self.text = text // 초기화 때 상수 프로퍼티에 값을 할당가능
    }
    
    func ask() {
        print(text)
    }
}

let cheeseQuestion = SurveyQuestion(text: "Are you cheese?")

<note> 클래스 인스턴스의 상수 프로퍼티는 그 클래스의 이니셜라이저에서만 값 할당이 가능하고 서브클래스에서는 불가능하다.



Default Initializers

Swift는 구조체/클래스가 다음 사항들을 만족할 경우 디폴트 이니셜라이저를 제공한다. (1) 모든 프로퍼티에 디폴트 값이 정의되어있고 (2) 커스텀 이니셜라이저가 하나도 정의되어 있지 않을 때.

디폴트 이니셜라이저는 모든 프로퍼티가 디폴트값으로 셋팅된 인스턴스를 생성해준다.

class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}

var item = ShoppingListItem()


그런데 커스텀 이니셜라이저를 정의하면, 디폴트 이니셜라이저가 제공되지 않는 이유 무엇일까?

-> 커스텀 이니셜라이저를 정의한 이상, 그것을 통해서만 완전한 초기셋팅이 가능할지도 모른다. 따라서 실수로 디폴트 이니셜라이저로 인스턴스를 만들어서 불완전한 초기셋팅이 될지도 모르는 상황을 미리 방지해주는 것이다. 따라서 커스텀 이니셜라이저와 디폴트 이니셜라이저를 둘 다 쓰고 싶은 경우는 확장Extension을 통해 커스텀 이니셜라이저를 구현하는 것을 추천한다.



<구조체에서 쓰이는 멤버단위 이니셜라이저Memberwise Initializers for Structure Types>

구조체에 커스텀 이니셜라이저가 정의되지 않았다면 자동으로 Memberwise 이니셜라이저가 제공된다. Stored 프로퍼티에 디폴트값이 없더라도 제공된다.

struct Size {
    var width = 0.0
    var height: Double
}
let twoByTwo = Size(width: 2.0, height: 2.0)




Initializer Delegation for Value Types

이니셜라이저 딜리게이션initializer delegation이란 이니셜라이저 안에서 다른 이니셜라이저를 호출하는 것을 말한다. 이것을 통해 이니셜라이저가 여러 개일때 코드의 중복을 줄일 수 있다.

이니셜라이저 딜리게이션의 룰은 사용할 타입이 value 타입인지 reference 타입인지에 따라 달라진다. value 타입은 단순히 다른 이니셜라이저가 제공하는 것들을 대리수행delegation만 하면 된다. 그러나 refrence 타입, 즉 클래스 같은 타입들은 상속이 가능하기 때문에 상위 클래스의 초기셋팅까지도 잘 되었는지를 보장할 필요가 있으므로 까다로워지는 것이다. 일단 여기서는 value 타입만 살펴보자.

커스텀 이니셜라이저를 value 타입에 정의하고 그 안에서 다른 이니셜라이저를 호출하려면 self.init 을 호출하면 된다. 예를 보자.

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()
    
    // 3개의 이니셜라이저를 정의하고 있다
    
    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)
        // 다른 이니셜라이저를 호출해주고 있다
        // 참고로 self.init 호출은 이니셜라이저에서만 가능하다
    }
}

// (1) 디폴트 이니셜라이저와 같은 동작
let basicRect = Rect()

// (2) memberwise 이니셜라이저와 같은 동작
let originPoint = Point()
let rectSize = Size(width: 5.0, height: 5.0)
let originRect = Rect(origin: originPoint, size: rectSize)

// (3) 좀 더 복잡한 이니셜라이저를 사용
let centerPoint = Point(x: 2.5, y: 2.5)
let centerRect = Rect(center: centerPoint, size: rectSize)


이처럼 value 타입의 이니셜라이저 딜리게이션은 간단하다. 그러나 클래스 타입 (reference type)에 대해서는 여러가지 살펴볼 사항들이 있다. 다음 시간에는 클래스의 상속과 초기화에 대해 중점적으로 다뤄보도록 하겠다.


 2편을 작성하였습니다 > http://wlaxhrl.tistory.com/19

'Swift 공식 가이드 > Swift 2' 카테고리의 다른 글

Initialization (3/3)  (0) 2016.05.07
Initialization (2/3)  (0) 2016.05.06
Inheritance  (0) 2016.04.24
Subscripts  (0) 2016.04.23
Methods  (0) 2016.04.17
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함