[Swift] init(초기화) (1/2)

·2024년 7월 8일
0

Swift 문법

목록 보기
13/16

초기화

  • 타입의 저장된 프로퍼티에 초기값을 세팅
  • 초기화 구문을 정의하여 초기화 구현
  • 처음 사용되기 전에 타입의 새로운 인스터스가 올바르게 초기화되는 것을 보장하는 것이 주요 역할

<1편에서 알아볼 것>
기본적인 초기화 개념
Value type의 initializer delegation


저장 프로퍼티에 초기값 설정

  • 클래스나 구조체 인스턴스가 생성될 때까지 모든 저장 프로퍼티에 반드시 초기값을 설정해야 함
  • 저장 프로퍼티에 초기값을 설정하는 방법은 2가지 존재
    • 프로퍼티를 정의할 때 기본값을 할당하기
    • 초기화 구문을 사용하여 구문 내에서 프로퍼티에 초기값을 설정하기

초기화 구문

선언

init() {
	// 초기화 구문
}

ex)

class Student {
    var name: String
    
    init() {
        name = "Mini"
        print("Student class initialization")
    }
}

var student = Student()
student // Student class initialization

name 프로퍼티를 선언하고 초기화 구문 내에서 값을 설정함

기본 프로퍼티 값 설정하기

struct Student {
    var name = "Mini"
}

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

프로퍼티가 항상 같은 초기값을 같는다면 초기화 구문 내에서 값을 설정하기보다 기본값을 제공하는 것도 고려해볼 수 있다.



초기화 구문 사용자화

초기화 파라미터

  • 초기화를 여러 형식으로 변형할 수 있는데 그중 하나가 바로 초기화 파라미터를 이용하는 것
class SomeClass {
    var someProperty: Int

    init(overthreeProperty: Int) {
        someProperty = overthreeProperty - 3
    }

    init(for overFourProperty: Int) {
        someProperty = overFourProperty - 4
    }

    init(_ overFiveProperty: Int) {
        someProperty = overFiveProperty - 5
    }
}

let someClassA = SomeClass(overthreeProperty: 5)
someClassA.someProperty // 2
let someClassB = SomeClass(for: 5)
someClassB.someProperty // 1
let someClassC = SomeClass(5)
someClassC.someProperty // 0


옵셔널 프로퍼티 타입

  • 타입이 초기화 동안에 값을 설정할 수 없거나, 논리적으로 값이 없는 상태를 가질 수 있는 경우 옵셔널 타입으로 선언함
  • 옵셔널 프로퍼티는 nil로 자동 초기화된다!
class SurveyQuestion {
    var text: String
    var response: String?
    
    init(text: String) {
        self.text = text
    }
}

let question = SurveyQuestion(text: "Name?")


상수 프로퍼티에 할당하기

  • 초기화 구문 내에서 상수 프로퍼티에 값을 할당하기
class SurveyQuestion {
    let text: String
    var response: String?
    
    init(text: String) {
        self.text = text
    }
}

let question = SurveyQuestion(text: "Name?")
question


기본 초기화 구문

초기화 구문을 만들지 않은 모든 구조체 또는 클래스에 Swift는 기본 초기화 구문을 제공한다.

기본 초기화 구문은 모든 프로퍼티가 기본값으로 설정된 새로운 인스턴스를 생성함.

class Car {
    var modelName = "BMW X5"
    var ownerName: String?
    var purchased = false
}

let car = Car()
  • 기본값이 존재하는 modelName, purchased
  • 옵셔널 String 타입의 ownerName 은 nil로 기본값 설정
  • 모든 프로퍼티에 기본값을 가지고 있는 Car 클래스는 기본 초기화 구문을 제공받음
    -> 기본 초기화 구문을 사용하여 새로운 인스턴스를 생성한 과정

구조체 부분을 잘 공부했다면 떠오를 구조체의 멤버와이즈 초기화 구문!

구조체에서는 모든 프로퍼티에 기본값을 설정해주지 않고, 초기화 구문을 따로 구현해 주지 않아도 멤버와이즈 초기화 구문을 사용하여 인스턴스를 생성할 수 있었다.


구조체 타입의 멤버와이즈 초기화 구문(memberwise initializer)

  • 새로운 구조체 인스턴스에 멤버 프로퍼티를 초기화 하기 위한 짧은 구문
  • 구조체는 사용자화 초기화 구문을 따로 지정하지 않으면 자동적으로 멤버와이즈 초기화 구문을 받음.
  • 기본 값을 가지지않는 저장 프로퍼티는 멤버와이즈 초기화 구문에서 무조건 초기화 필요 (당연)

  1. 모든 프로퍼티가 기본 값을 가지지 않는 경우

    struct Size {
        var width: Double
        var height: Double
    }
    
    let twoByTwo = Size(width: 2.0, height: 2.0)
    let threeByThree = Size() // error!
    // 🚨 Missing arguments for parameters 'width', 'height' in call
    let fourByFour = Size(width: 4.0) // error!
    // 🚨 Missing argument for parameter 'height' in call
  1. 기본 값을 가지지 않는 프로퍼티가 존재하는 경우

    struct Size {
        var width: Double
        var height = 3.0
    }
    
    let twoByThree = Size(width: 2.0)
    let threeByFour = Size(height: 4.0) // error!
    // 🚨 Missing argument for parameter 'width' in call
  2. 모든 프로퍼티가 기본 값을 가지는 경우

    struct Size {
        var width = 2.0
        var height = 2.0
    }
    
    let twoByTwo = Size() // Size(width: 2.0, height: 2.0)
    let threeByThree = Size(width: 3.0, height: 3.0) // Size(width: 3.0, height: 3.0)
    let twoByThree = Size(height: 3.0) // Size(width: 2.0, height: 3.0)

Initializer Delegation - Value Types

초기화 구문은 인스턴스의 초기화 중,
중복된 코드를 방지하기 위해서 다른 초기화 구문을 호출할 수 있음

이 프로세스를 초기화 구문 위임(initializer delegation)이라고 부른다.

오히려 한국어가 더 어려워보여서 이 글에서는 initializer delegation이라 부르겠다.

initializer delegation는 value type, reference type에 따라 동작하는 방식이 다르다.

결론부터 이야기하자면 상속이라는 개념 때문인데, 우선 value type의 경우부터 이야기해 보겠다.


value type은 상속이라는 개념이 존재하지 않으므로 다른 타입이 아닌 자신의 다른 initializer에만 initializer를 위임할 수 있다.

따라서 다른 value type에 initializer를 위임하려면, self.init 을 호출하면 된다.

struct Center {
    var x: Int
    var y: Int
    
    init(centerX: Int, centerY: Int) {
        x = centerX
        y = centerY
    }
    
    init(center: Int) {
        x = center
        y = center
    }
}

let centerOne = Center(centerX: 10, centerY: 10)
print(centerOne) // Center(x: 10, y: 10)
let centerTwo = Center(center: 3)
print(centerTwo) // Center(x: 3, y: 3)

위의 두 initializer을 잘 보면 두 번째 initializer은 첫 번째와 중복된 코드인 것이 느껴질 것.

initializer delegation은 코드가 중복되는 것을 방지한다고 했었다.

struct Center {
    var x: Int
    var y: Int
    
    init(centerX: Int, centerY: Int) {
        x = centerX
        y = centerY
    }
    
    init(center: Int) {
        self.init(centerX: center, centerY: center)
    }
}

let centerThree = Center(center: 7)
print(centerThree) // Center(x: 7, y: 7)

따라서 중복된 코드를 제거하기 위해 init(centerX: Int, centerY: Int) 에 위임할 수 있다.

+)

ref) 예제(https://babbab2.tistory.com/169)

if) initializer를 변수 y는 무조건 0으로 초기화하는 것으로 수정해줘 라고 요청이 들어왔다면!

initializer delegation을 하지 않은 경우

struct Center {
    var x: Int
    var y: Int
    
    init(centerX: Int, centerY: Int) {
        x = centerX
        y = 0
    }
    
    init(center: Int) {
        x = center
        y = 0
    }
}

initializer delegation을 한 경우

struct Center {
    var x: Int
    var y: Int
    
    init(centerX: Int, centerY: Int) {
        x = centerX
        y = 0
    }
    
    init(center: Int) {
        self.init(centerX: center, centerY: center)
    }
}

이런 식으로 유지보수가 훨씬 쉬워진다.

예시는 간단한 코드라서 쉬워진다고 표현하기도 어렵지만, 복잡한 코드에서는 정말 좋을 것 같다. ✨


0개의 댓글

관련 채용 정보