Swift의 생성자

소혜 (Sohye)
xohxe (김소혜)· May 02, 2024

기초 중에 기초 생성자 Initalizer, 하지만 생각보다 잘 모르고 쓰는 것들이 많기에 공식문서를 중심으로 내용을 정리해보았습니다.

그래서 이번 글에서는 클래스, 구조체, 열거형의 초기화에 대해 나눠 알아보고, 필수생성자(required)와 실패가능한 생성자(failable)에 대해서도 알아보겠습니다.

1. 생성자(Initalizer)란?

'생성자(Initalizer)'객체의 인스턴스를 생성하는 메서드로 모든 프로퍼티를 기본 값으로 초기화 하는 역할을 합니다. Swift에서는 클래스뿐만 아니라 구조체와 열거형에 대해서도 생성자를 지원합니다.

Swift에서는 init() 형태로 작성되는데, 예제 코드를 살펴봅시다.

COPY
struct Person {
	let name: String
	let age: Int

	init(name: String){ // initalizer로 프로퍼티 초기화
		self.name = name
		self.age = 29
	}
}

let phang: Person = Person(name: "phang") // "phang"이라는 인스턴스 생성

먼저 init 에서 프로퍼티가 초기화되고, phang를 지정하면서 인스턴스가 생성되게 됩니다.

그런데 인스턴스 안에 기본값이 존재하지 않는 프로퍼티가 있다면, 초기화에 실패하여 오류가 발생하게 됩니다. (이 말은 인스턴스가 생성되지 않는 말과 같습니다.)

init 에 대해서... init는 메서드이지만 func 키워드를 사용하지 않고 Init라는 키워드를 사용하며 선언하며, 반환값도 존재하지 않습니다.

객체의 부연설명 객체는 일반적으로 클래스를 통해 추상화된 형태로 description을 작성합니다. 그리고 클래스를 실제로 사용하기 위해서는 실제 메모리에 올라가있는 객체의 인스턴스를 만들어야합니다. 이 때, 객체의 인스턴스를 만드는 메소드가 생성자입니다.

먼저 초기화, 생성자 두 개념이 혼동될수 있으니 생성자의 큰 개념인 초기화 부터 먼저 알아볼게요.

2. 구조체의 초기화

구조체를 초기화하는 방법을 하나하나 알아봅시다.

2-1. 기본값

구조체의 선언과 동시에 프로퍼티에 기본값을 넣어주면 초기화가 됩니다.

COPY
struct Bus {
	let region: String = "Seoul"
	let number: Int = 01
}

2-2. 옵셔널

구조체의 프로퍼티의 타입을 옵셔널(Optional) 타입으로 설정하는 것 또한 초기화를 진행해준 것입니다.

COPY
struct Bus {
	let region: String?
	let number: Int?
}

이런 코드는 값이 있는지 없는지 모르는 경우에 사용하면 좋습니다. 그럼 자동으로 region, number의 값은 nil 로 초기화가 됩니다.

2-3. init() 생성자

init 함수(생성자)를 통해서 초기화를 진행할 수도 있습니다.

COPY
struct Person {
	let name: String
	let age: Int

	init(name: String){ // initalizer로 프로퍼티 초기화
		self.name = name
		self.age = 29
	}
}

그런데 struct을 사용할 때를 생각해보면 init함수를 사용하지 않아도 잘 사용했었잖아요?

구조체에서는 memberwise initializer라는 초기화를 기본적으로 제공해서 생성자를 별도로 작성하지 않아도 되기 때문입니다.

COPY
struct Person {
	let name: String
	let age: Int
}
// 별도로 작성하지 않아도 memberwise initializer가 제공됩니다.
let myFriend = Person(name:"manchae", age: 24)

구조체에서 init 함수 사용시 주의할 점

잠깐! 이 때 제공되는 생성자의 파라미터는 프로퍼티가 선언된 순서에 맞춰서 진행되기 때문에, 프로퍼티가 선언된 순서를 보고 작성해야합니다.

COPY
Person.init(age:22, name:"hihi") // error!

그리고 init 메소드를 별도로 작성하게 되면 memberwise initializer는 사용할 수 없게되어 모든 프로퍼티 값을 정해줘야합니다.

만약 init 함수도 쓰고  memberwise initializer도 사용하고 싶다면, extension 안에 생성자를 쓰면 memberwise initializer를 그대로 사용할 수 있습니다.

COPY
struct Person {
	let name: String
	let age: Int
}

extension Person {
	init(name: String){
		self.name = name
		self.age = 29
	}
}

Person.init(name:"xohxe")
Person.init(name:"sungjin", age: 25)

그리고 선언된 프로퍼티 중 하나라도 private를 사용하면, memberwise initializer는 제공되지 않습니다.

COPY
struct Person {
	private var name: String
	let age: Int
}

3. 클래스의 초기화

3-1. 기본값

구조체와 동일하게 직접 선언과 동시에 기본값을 넣어서 초기화할수 있습니다.

COPY
class Person {
	let name: String = "hello"
	let age: Int = 23
}

3-2. 옵셔널

옵셔널 타입의 변수로 하면 nil 값으로 초기화됩니다.

COPY
class Person {
	var name: String?
	var age: Int?
}

그런데 let으로 선언을 하면, nil 값 외에는 가질수 없으니 오류가 발생하지 않을까요? 그렇기 때문에 상수 let이 아닌 변수 var로 선언해주어야합니다.

COPY
class Person {
	let name: String? // Error!!
}

3-3. 생성자 init()

앞서 정리해봤던 것들을 적용해서 코드를 작성해보았는데, init 생성자가 종료되기 전까지 모든 프로퍼티가 값을 가질 수 있도록 init()내 코드를 작성해주면 됩니다.

COPY
class Person {
	var name: String? // 옵셔널이라 생성자 따로 선언할 필요 없음.
	let nickName: Stirng = "xohxe"
	let age: Int

	init(name: String){
		self.age = 29
	}
}

여기부터는 아직 정리되지 않은 내용들이 있어서, 지속해서 업데이트 예정입니다..🥹

4. 지정생성자와 편의생성자

Swift는 클래스의 저장 프로퍼티들이 반드시 값을 가질 수 있도록 두 가지 생성자를 정의했는데, 그것이 바로 지정 생성자와 편의 생성자입니다.

COPY
init ( parameters ) {
	// statements
}
// designated Initializer

convenience init ( parameters) {
 	// statements
}
// convenience Initializer

4-1. 지정 생성자(Designated Initializer)

지정 생성자는 클래스의 기본적인 생성자로 클래스의 모든 프로퍼티를 초기화하고, 정확한 생성자 프로세스를 수행하기 위해 super 클래스 생성자를 호출하여 슈퍼 클래스의 프로퍼티들도 초기화합니다. 클래스에는 하나의 지정 생성자가 있는 것이 일반적이며, 모든 클래스는 적어도 하나의 지정 생성자는 있어야합니다.

4-2. 편의 생성자(Convenience Initializer)

편의 생성자는 지정 생성자의 초기화를 보조해주는 역할을 합니다. 클래스의 특수한 인스턴스를 만들 때 사용하면 초기화 과정을 간소화할 수 있습니다.

4-3. Initializer Delegation

지정 생성자와 편의 생성자의 관계를 간단하게 하기 위해, Swift에서는 생성자의 Delegation에 세 가지 규칙을 적용하고 있습니다.

  • 규칙 1: 지정 생성자는 호출 즉시 슈퍼 클래스의 지정 생성자를 호출한다.
  • 규칙 2: 편의 생성자는 같은 클래스 내의 다른 생성자를 호출해야한다.
  • 규칙 3: 편의 생성자는 지정 생성자를 호출해야만 한다.

5. 필수생성자와 실패가능 생성자

5-1. 필수 생성자(Required Initalizers)

COPY
class AClass { // 부모클래스

    required init() { // 필수생성자
        print("필수구현")
    }
}

class BClass : AClass{
	required init(){

	}
}

위의 코드처럼 부모 클래스에서 생성자 앞에 required 키워드를 사용하면 서브 클래스에서 반드시 해당 생성자를 구현해야합니다. 이를 필수 생성자라고 하며, 이 생성자를 오버라이드 할때는 override 키워드를 붙이지 않아도 됩니다.

5-2. 실패 가능 생성자(Failable Initalizers)

기존 생성자는 컴파일 시점에 모든 프로퍼티가 초기화되지 않는다면, 컴파일 에러가 발생하게 됩니다. 이러한 에러들을 방지하기 위해 상황에 따라 실패가능생성자를 유용하게 사용할 수 있습니다. 파라미터 값이 잘못되었거나 리소스가 없는 경우 등 어딘가에 문제가 발생해서 인스턴스를 생성할 수 없게 되었을 때, init대신 init? 으로 사용하면 생성 실패시 nil 을 반환하여 에러를 방지할 수 있습니다.

실패가능 생성자는 클래스, 구조체, 열거형 모두 적용가능하며, 유의할 점은 실패 시 return nil 이라고 성공했을 때 return 을 사용하면 안됩니다. swift의 생성자는 인스턴스를 반환하는 형식이 아니기 때문입니다.

COPY
class AClass { // 부모클래스

    var str: String

    init?(str: String) {
        if str.isEmpty {    // str이 빈값이면
            return nil      // return nil
        } else {            // 빈값이 아니면
            self.str = str  // 초기화
        }
    }
}

// nil 가능성이 있기때문에 옵셔널타입이다
let ac: AClass? = AClass(str: "")       // nil
let ac2: AClass? = AClass(str: "Hello")
ac2?.str                                // "Hello"
이전 게시물
Combine 프레임워크와 RxSwift

© 2023 - 2024 xohxe. All Rights Reserved.