study record

[Swift] 스위프트 프로그래밍 - 인스턴스 생성과 프로퍼티 본문

Swift/스위프트 프로그래밍

[Swift] 스위프트 프로그래밍 - 인스턴스 생성과 프로퍼티

asong 2021. 11. 7. 01:18

인스턴스 생성

초기화 과정은 새로운 인스턴스를 사용하기 위하여 저장 프로퍼티의 초깃값을 설정하는 등의 일을 한다. 이니셜라이저를 정의하면 초기화 과정을 직접 구현할 수 있다. 이렇게 구현된 이니셜라이저는 새로운 인스턴스를 생성할 수 있는 특별한 메서드가 된다. 스위프트의 이니셜라이저는 반환 값이 없다. 그저 초기화하는 용도이다.

이니셜라이저는 func 키워드를 사용하지 않고 오로지 init 키워드를 사용하여 이니셜라이저 메서드임을 표현한다. init 메서드는 클래스, 구조체, 열거형 등의 구현부에 위치한다. 다만 클래스의 지정 이니셜라이저는 익스텐션에서 구현해줄 수 없다.

class SomeClass {
	init() {
		// 초기화할 때 필요한 코드
	}
}

struct SomeStruct {
	init(){

	}
}

enum SomeEnum {
	case someCase

	init(){
// 열거형은 초기화할 때 반드시 case 중 하나가 되어야 한다.
		self = .someCase
	}
}

 

프로퍼티 기본값

구조체와 클래스의 인스턴스는 처음 생성할 때 옵셔널 저장 프로퍼티를 제외한 모든 저장 프로퍼티에 적절한 초깃값을 할당해야 한다. 이니셜라이저가 실행될 때 저장 프로퍼티에 적절한 초깃값을 할당할 수 있다. 초기화 후에 값이 확정되지 않은 저장 프로퍼티는 존재할 수가 없다. 프로퍼티를 정의할 때 프로퍼티 기본값을 할당하면 이니셜라이저에서 따로 초깃값을 할당하지 않아도 초기화가 된다.

 

//이니셜라이저를 통해 초깃값 할당 
struct Area{
	var sauareMeter: Double
	init(){
		squareMeter = 0.0
	}
}

// 프로퍼티 기본값을 지정하는 경우
struct Area{
	var sauareMeter: Double = 0.0 
}

 

이니셜라이저 매개변수

인스턴스를 초기화하는 과정에서 필요한 값을 전달받을 수 있다. 따로 전달인자 레이블이 필요하지 않은 경우에는 와일드카드 식별자(_)를 사용하여 전달인자 레이블을 없애주면 된다.

 

옵셔널 프로퍼티 타입

초기화 과정에서 꼭 값을 초기화하지 않아도 되는, 인스턴스가 사용되는 동안에 값을 갖지 않아도 되는 저장 프로퍼티가 있을 경우 옵셔널로 선언할 수 있다. 초기화 과정에서 값을 지정해주기 어려운 경우 저장 프로퍼티를 옵셔널로 선언할 수도 있다. 초기화과정에서 값을 할당해주지 않으면 자동으로 nil이 할당된다.

 

상수 프로퍼티

상수로 선언된 저장 프로퍼티는 인스턴스를 초기화하는 과정에서만 값을 할당할 수 있으며, 처음 할당된 이후에는 값을 변경할 수 없다.

 

 

기본 이니셜라이저와 멤버와이즈 이니셜라이저

사용자 정의 이니셜라이저를 정의해주지 않으면 클래스나 구조체는 모든 프로퍼티에 기본값이 지정되어 있다는 전제하에 기본 이니셜라이저를 사용한다. 기본 이니셜라이저는 프로퍼티 기본값으로 프로퍼티를 초기화해서 인스턴스를 생성한다. 즉, 기본 이니셜라이저는 저장 프로퍼티의기본값이 모두 지정되어 있고, 동시에 사용자 정의 이니셜라이저가 정의되어 있지 않은 상태에서 제공된다.

저장 프로퍼티를 선언할 때 기본값을 지정해주지 않으면 이니셜라이저에서 초깃값을 설정해야 한다. 그러나 프로퍼티 하나 때문에 매번 이니셜라이저를 추가하거나 변경하는 일은 여간 귀찮다. 때문에 구조체는 사용자 정의 이니셜라이저를 구현하지 않으면 프로퍼티의 이름으로 매개변수를 갖는 이니셜라이저인 멤버와이즈 이니셜라이저를 기본으로 제공한다. 그렇지만 클래스는 멤버와이즈 이니셜라이저를 지원하지 않는다.

struct Point {
	var x: Double = 0.0
	var y: Double = 0.0
}

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

let point: Point = Point(x: 0, y: 0)
let size: Size = Size(width: 50.0, height: 50.0)

 

초기화 위임

값 타입인 구조체와 열거형은 코드의 중복을 피하기 위하여 이니셜라이저가 다른 이니셜라이저에게 일부 초기화를 위임하는 초기화 위임을 간단하게 구현할 수 있다. 하지만 클래스는 상속을 지원하기 때문에 간단한 초기화 위임도 할 수 없다.

값 타입에서 이니셜라이저가 다른 이니셜라이저를 호출하려면 self.init을 사용한다. 당연히 self.init 은 이니셜라이저 안에서만 사용할 수 있다. self.init을 사용한다는 것 자체가 사용자 정의 이니셜라이저를 정의하고 있다는 뜻이다. 사용자 정의 이니셜라이저를 정의하면 기본 이니셜라이저와 멤버와이즈 이니셜라이저를 사용할 수 없다. 따라서 초기화 위임을 하려면 최소 두 개 이상의 사용자 정의 이니셜라이저를 정의해야 한다.

enum Student {
	case elementary, middle, high
	case none

// 사용자 정의 이니셜라이저가 있는 경우, init() 메서드를 구현해야 기본 이니셜라이저를 사용할 수 있다.
init() {
	self = .none
}

init(koreanAge: Int) {
	switch koreanAge {
	case 8...13:
		self = .elementary
	case 14...16:
		self = .middle
	case 17...19:
		self = .high
	default:
		self = .none
	}
}
	init(bornAt: Int, currentYear: Int) {
		self.init(koreanAge: currentYear - bornAt +1)
	}
}

var younger: Student = Student(koreanAge: 16)
younger = Student(bornAt: 1998, currentYear: 2016)

 

이 열거형은 두 개의 사용자 정의 이니셜라이저가 있다. 두 번째 사용자 정의 이니셜라이저는 태어난 해와 현재 연도를 전달받아 나이로 계산한 후 첫 번째 이니셜라이저에 초기화를 위임한다. 이렇게 초기화 위임을 사용하여 코드를 중복으로 쓰지 않고 효율적으로 이니셜라이저를 만들 수 있다.

 

실패 가능한 이니셜라이저

이니셜라이저의 전달인자로 잘못된 값이나 적절치 못한 값이 전달되었을 때, 이니셜라이저가 인스턴스 초기화에 실패할 수 있다. 이렇게 실패 가능성을 내포한 이니셜라이저를 실패 가능한 이니셜라이저라고 부른다. 실패 가능한 이니셜라이저는 실패했을 때 nil을 반환해주므로 반환 타입이 옵셔널로 지정된다. 따라서 init 대신에 init? 키워드를 사용한다.

 

인스턴스 소멸

클래스의 인스턴스는 디이니셜라이저를 구현할 수 있다. 메모리에서 해제되기 직전 클래스 인스턴스와 관련하여 원하는 정리 작업을 할 수 있다. 디이니셜라이저는 클래스의 인스턴스가 메모리에서 소멸되기 바로 직전에 호출된다. deinit 키워드를 사용하여 디이니셜라이저를 구현하면 자동으로 호출된다. 디이니셜라이저는 클래스의 인스턴스에만 구현할 수 있다.

클래스에는 디이니셜라이저를 단 하나만 구현할 수 있다. 디이니셜라이저는 이니셜라이저와는 다르게 매개변수를 갖지 않으며 소괄호도 적어주지 않는다. 자동으로 호출되므로 별도의 코드로 호출할 수 없다. 또한 디이니셜라이저는 인스턴스를 소멸하기 직전에 호출되므로 인스턴스의 모든 프로퍼티에 접근할 수 있고 변경할 수도 있다.

class FileManager {
	var fileName: String
	
	init(fileName: String) {
		self.fileName = fileName
	}

	deinit{
		print("deinit")
	}
}