티스토리 뷰

iOS

[Swift] 초기화 (Initialization) 1/3

Peppo 2022. 4. 20. 21:41
728x90

초기화

 

초기화는 클래스, 구조체, 열거형 인스턴스를 사용하기 위해 준비 작업을 하는 단계 입니다.

이 단계에서 각 저장 프로퍼티의 초기 값을 설정 합니다. 

initializer를 정의하여 초기화 과정을 실행 시킬 수 있습니다. 

Swift의 initializer는 값을 반환하지 않습니다.

초기화와 반대로 여러 값과 자원의 해지를 하기 위해 deinitializer를 사용합니다.

 


저장 프로퍼티를 위한 초기값 세팅 (Setting Initial Values for Stored Properties)

 

클래스와 구조체는 인스턴스가 생성될 때까지 저장프로퍼티를 적절한 초기값으로 세팅해야 합니다. 

NOTE

저장프로퍼티에 기본값을 할당하거나, initializer에 초기값을 세팅하면,
프로퍼티 옵저버가 호출되지 않고 해당 프로퍼티의 값이 직접 세팅 됩니다.

 

이니셜라이저 (Initializers) 

 

이니셜라이저는 특정 타입의 새로운 인스턴스를 생성 합니다.

가장 간단한 형태로, 파라미터가 없는 인스턴스 메소드의 형태와 같습니다.

이니셜라이저는 init 키워드를 사용합니다.

 

init() {
  // perform some initialization here
}

 

아래는 섭씨 온도 (Celsius) 구조체를 만들어 온도(temperature) 라는 프로퍼티를 선언하고, 

이니셜라이저에서 초기화 하는 코드 입니다.

struct Celsius {
    var temperature: Double
    init() {
        temperature = 36.5
    }
}

var celsius = Celsius()
print("사람의 정상 체온은 \(celsius.temperature) 입니다.")
// 사람의 정상 체온은 36.5 입니다.

 

기본 프로퍼티 (Defualt Property Values)

 

프로퍼티의 선언과 동시에 값을 할당하여 초기값으로 사용할 수 있습니다.

 

항상 같은 초기값을 갖는다면 기본 프로퍼티를 사용하는것이 좋습니다.

프로퍼티에 굳이 타입을 선언하지 않아도 타입추론 으로 초기값을 참조해서 사용할 수 있기 때문!

struct Celsius {
    var temperature = 36.5
    // init 사용 x
}

 


커스터마이징 초기화 (Customizing Initialization)

 

초기화 과정에 인풋 파라미터와, 옵셔널 프로퍼티 타입 혹은 상수 값을 할당해서 커스터마이징 할 수 있습니다. 

 

 

초기화 파라미터 (Initialization Parameters)

 

초기화가 정의된곳에 파라미터를 넣어서 사용할 수 있습니다. 

 

아래는 프로퍼티를 초기화 파라미터로 입력받아 초기화에 사용하는 예제 입니다. 

// Celsius: 섭씨 (한국 온도단위), Fahrenheit: 화씨 (미국 온도단위)
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

 

 

파라미터 이름과 인자 레이블 (Parameter Names and Argument Labels)

 

이니셜라이저는 메소드와 다르게 파라미터를 이용해 이니셜라이저를 식별합니다.  (메소드는 메소드명으로 식별)

 

아래는 초기화 시 파라미터를 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
    }
}

// 인자값 3개 (red, green, blue)를 받는 경우
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)

// 인자값 1개 (white)를 받는 경우
let halfGray = Color(white: 0.5)

// 인자값을 지정해주지 않는경우 (error)
let veryGreen = Color(0.0, 1.0, 0.0)  // error 인자값을 지정을 안해줬어!!

 

 

인자 레이블이 없는 이니셜라이저 파라미터
(
Initializer Parameters Without Argument Labels)

 

인자 레이블을 생략하는게 더 명확한 경우 _ (underbar) 기호를 사용하여,

이니셜라이저에서 인자 레이블을 생략할 수 있습니다.

 

struct Celsius {
    var temperatureInCelsius: Double
    
    init(_ celsius: Double) {
        temperatureInCelsius = celsius
    }
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0

 

 

옵셔널 프로퍼티 타입 (Optional Property Types)

 

프로퍼티의 최초 값이 없고 나중에 추가될 수 있는 값을 옵셔널로 선언해 사용할 수 있습니다. 

옵셔널 프로퍼티는 자동으로 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  // nil 
cheeseQuestion.response = "Yes, I do like cheese."
cheeseQuestion.response  // Yes, I do like cheese

 


 

기본 이니셜라이저 (Default Initializers)

 

만약 모든 프로퍼티의 초기값이 설정되어 있다면,

Swift는 모든 프로퍼티를 자동으로 기본값을 초기값으로 초기화 합니다.

class ShoppingListItem {
    // 모든 프로퍼티에 초기값 설정
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()
// nil
// quantity 1
// purchased false

 

 

Memberwise 이니셜라이저 (Memberwise Initializers for Structure Types)

 

memberwise 이니셜라이저는 프로퍼티의 기본값이 없어도 커스텀 이니셜라이저를 정의하지 않았으면 

memberwise 이니셜라이저를 제공해줍니다. 

이 초기자(initializer)는 선언한 모든 프로퍼티를 인자로 사용합니다.

 

쉽게 얘기해서, 

 

Struct 에만 존재하는 initializer이며,

struct에 저장된 프로퍼티들을 객체가 생성될때 필요한 인자들을 자동으로 완성시켜주는걸 말합니다.

아래와 같은 상황처럼요.

struct Size {
    var width = 0.0
    var height = 0.0
}

 

 

struct Size 내부를 아래와 같이 바꿔보겠습니다.

 

 

 


 

값 타입을 위한 이니셜라이저 위임 (Initializer Delegation for Value Types)

 

이니셜라이저에서 다른 이니셜라이저를 호출할 수 있는데, 이 과정을 이니셜라이저 위임 이라고 합니다. 

값 타입 (struct, enum)과 참조 타입 (class) 에 따라 다르게 동작 합니다.

 

값 타입은 상속을 지원하지 않기 때문에 단순하게 다른 이니셜라이저가 제공해준 것으로 대신합니다.

같은 값 타입에서 다른 이니셜라이저를 가져올수 있도록 self.init을 사용할 수 있습니다. 

self.init은 이니셜라이저 안에서만 호출할 수 있습니다.

 

참조 타입은 상위 클래스로부터 상속을 받아 하위 클래스에서 호출이 가능 합니다.

이 의미는 초기화 중에 상속받는 모든 저장 프로퍼티에 값이 맞는지 보장해야한다는 책임을 갖는다는 뜻입니다.

 

NOTE 

커스텀 이니셜라이저를 사용하면서 기본 이니셜라이저, memberwise 이니셜라이저도 사용하고 싶다면,
커스텀 이니셜라이저를 extension에서 구현하면 됩니다.

 

아래는 사각형 구조체에 필요한 두 개의 구조체 Size, Point가 모든 프로퍼티에 기본값 0을 갖는 예제 입니다.

 

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()
    // 1
    init() {}   
    // 2
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    // 3
    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)
    }
    
}

구조체 Rect에는 3개의 이니셜라이저가 있습니다. 

 

 

 

첫번째 이니셜라이저 init()은 기본 이니셜라이저와 같은 기능으로 프로퍼티에 정의된 기본값으로 초기화 됩니다.

let basicRect = Rect()
// basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)

 

두번째 이니셜라이저 init(origin:,size:)는 memberwise 이니셜라이저와 같은 기능으로 origin 과 size 프로퍼티에 할당합니다.

//   init(origin: Point, size: Size) {
//        self.origin = origin
//        self.size = 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)

 

세번째 이니셜라이저 init(center:size:)는 내부에서 다른초기자인 init(origin:, size:)를 사용하여 위임합니다.

// init(center: Point, size: Size) {
//        let originX = center.x - (size.width / 2)   // originX = 4 - 1.5
//        let originY = center.y - (size.height / 2)  // originY = 4 - 1.5
//        self.init(origin: Point(x: originX, y: originY), size: 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)

여기까지 init 초기화에 대한

기본적인 개념을 알아보았고

 

다음엔 

 

지정 초기자 (designated initializer),

편리 초기자 (convenience initializer)에 대해 정리해보겠습니다.

728x90