11. 인스턴스 생성 및 소멸
- 초기화는 클래스나 구조체 또는 열거형의 인스턴스를 사용하기 위한 준비 과정
- 초기화가 완료된 인스턴스는 사용 후 소멸 시점이 오면 소멸함
11.1 인스턴스 생성
- 초기화: 새로운 인스턴스를 사용할 준비를 하기 위하여 저장 프로퍼티의 초기값을 설정하는 등의 일
- 이니셜라이저(Initializer)를 정의하면 초기화 과정을 직접 구현할 수 있음
- init 키워드를 사용하여 이니셜라이저 메서드임을 표현
11.1.1 프로퍼티 기본 값
- 옵셔널 저장 프로퍼티를 제외한 모든 저장 프로퍼티에 적절한 초기값을 할당해야 함
- 초기화 후 값이 확정되지 않은 저장 프로퍼티는 존재할 수 없음
11.1.2 이니셜라이저 매개변수
// 11.1.2 이니셜라이저 매개변수 struct Area{ var squareMeter :Double // 사용자 정의 이니셜라이저를 만들면 기존의 기본 이니셜라이저(init())은 별도로 구현하지 않는 이상 사용할 수 없음 init(fromPy py: Double){ squareMeter = py * 3.3058 } init(fromSquareMeter squareMeter: Double){ self.squareMeter = squareMeter } // 전달인자 레이블을 사용하지 않았음. 별다른 의미 없는 value라는 이름의 매개변수로 있으므로, 전달인자 레이블 value가 필요하지 않다면 // 네 번째 이니셜라이저처럼 와일드카드 식별자(_)를 사용하여 전달인자 레이블을 없애줄 수 있음 init(value: Double){ squareMeter = value } // 와일드카드 식별자(_)를 사용하여 전달인자 레이블을 없앰 init(_ value: Double){ squareMeter = value } } let roomOne: Area = Area(fromPy: 15.0) print(roomOne.squareMeter) let roomTwo: Area = Area(fromSquareMeter: 33.06) print(roomTwo.squareMeter) let roomFour: Area = Area(value: 55.0) print(roomFour.squareMeter) // 네번째 이니셜라이저를 사용. 전달인자 레이블을 없앰 let roomThree: Area = Area(30.0) print(roomThree.squareMeter)
11.1.3 옵셔널 프로퍼티 타입
// 초기화 과정에서 값을 초기화하지 않아도 되는, 즉 인스턴스가 사용되는 동안에 값을 꼭 갖지 않아도 되는 저장 프로퍼티가 있다면 해당 프로퍼티를 옵셔널로 선언할 수 있음 // 또는 초기화 과정에서 값을 지정해주기 어려운 경우에도 저장 프로퍼티를 옵셔널로 선언할 수 있음 // 옵셔널로 선언한 저장 프로퍼티는 초기화 과정에서 값을 할당해주지 않는다면 자동으로 nil이 할당 됨 class Person{ var name: String var age: Int? init(name: String){ self.name = name // self.age 프로퍼티에 값을 할당해주지 않기때문에, 자동으로 nil이 할당 됨 } } let yagom: Person = Person(name: "yagom") print(yagom.name) print(yagom.age) // nil yagom.age = 33 print(yagom.age)
11.1.3 옵셔널 프로퍼티 타입
// 초기화 과정에서 값을 초기화하지 않아도 되는, 즉 인스턴스가 사용되는 동안에 값을 꼭 갖지 않아도 되는 저장 프로퍼티가 있다면 해당 프로퍼티를 옵셔널로 선언할 수 있음 // 또는 초기화 과정에서 값을 지정해주기 어려운 경우에도 저장 프로퍼티를 옵셔널로 선언할 수 있음 // 옵셔널로 선언한 저장 프로퍼티는 초기화 과정에서 값을 할당해주지 않는다면 자동으로 nil이 할당 됨 class Person{ var name: String var age: Int? init(name: String){ self.name = name // self.age 프로퍼티에 값을 할당해주지 않기때문에, 자동으로 nil이 할당 됨 } } let yagom: Person = Person(name: "yagom") print(yagom.name) print(yagom.age) // nil yagom.age = 33 print(yagom.age)
11.1.4 상수 프로퍼티
// 상수로 선언된 저장 프로퍼티는 인스턴스를 초기화하는 과정에서만 값을 할당할 수 있음. 처음 할당된 이후로는 값을 변경할 수 없음 // 클래스 인스턴스의 상수 프로퍼티는 프로퍼티가 정의된 클래스에서만 초기화 할 수 있음. 해당 클래스를 상속받은 자식클래스의 이니셜라이저에서는 부모클래스의 상수 프로퍼티 값을 초기화할 수 없음 class Person{ let name: String // 상수 프로퍼티 var age: Int? init(name: String){ self.name = name } } var yagom: Person = Person(name: "yagom") // yagom.name = "Eric" // 오류 발생! Cannot assign to property. 'name' is a 'let' constant
11.1.5 기본 이니셜라이저와 멤버와이즈 이니셜라이저
//11.1.5 기본 이니셜라이저와 멤버와이즈 이니셜라이저 // 사용자 정의 이니셜라이저를 정의해주지 않으면 클래스와 구조체는 모든 프로퍼티에 기본값이 지정되어 있다는 전제하에 기본 이니셜라이저를 사용함 // 기본 이니셜라이저는 프로퍼티 기본값으로 프로퍼티를 초기화하여 인스턴스를 생성함 // 즉, 기본 이니셜라이저는 저장 프로퍼티의 기본값이 모두 지정되어 있고, 동시에 사용자 정의 이니셜라이저가 정의되지 않은 상태에서 제공됨 // 구조체는 사용자 정의 이니셜라이저를 구현하지 않으면 프로퍼티의 이름으로 매개변수를 갖는 이니셜라이저인 멤버와이즈 이니셜라이저를 기본으로 제공함 // 클래스는 멤버와이즈 이니셜라이즈를 지원하지 않음 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: 5, y: 10) let size: Size = Size(width: 50, height: 20) // 구조체의 저장 프로퍼티에 기본값이 있는 경우 필요한 매개변수만 사용하여 초기화 할 수 있음 let somePoint: Point = Point() let someSize: Size = Size(width: 50) let anotherPoint: Point = Point(y: 100)
11.1.6 초기화 위임 (Initializer Delegation)
// 11.1.6 초기화 위임 (initializer Delegation) // 값 타입인 구조체와 열거형은 코드의 중복을 피하기 위해 이니셜라이저가 다른 이니셜라이저에게 일부 초기화를 위임하는 초기화 위임을 구현할 수 있음 // 참조 타입인 클래스는 상속을 지원하기 때문에 초기화 위임을 할 수 없음 enum Student{ case elementry, middle, high case none // 사용자 정의 이니셜라이저가 있는 경우, init() 메서드를 구현해주어야 기본 이니셜라이저를 사용할 수 있음 init(){ self = .none } init(koreanAge: Int){ switch koreanAge{ case 8...13: self = .elementry 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 lucy: Student = Student(koreanAge: 18) print(lucy) lucy = Student(bornAt: 2008, currentYear: 2022) print(lucy)
11.1.7 실패 가능한 이니셜라이저
// 11.1.7 실패 가능한 이니셜라이저(failable initializer) // 이니셜라이저를 통해 인스턴스를 초기화할 수 없는 여러가지 예외 상황이 있음. 대표적으로 이니셜라이저의 전달인자로 잘못된 값이 전달되었을 때. // 실패가능성을 내포한 이니셜라이저를 실패 가능한 이니셜라이저라고 부름 // 클래스, 구조체, 열거형 등에 모두 적용 가능 // 실패 가능한 이니셜라이저는 실패했을 때 nil을 반환해주므로 반한 타입을 옵셔널로 지정 // init 대신 init? 키워드 사용 class Person{ let name: String var age: Int? init?(name: String){ if name.isEmpty{ return nil } self.name = name } init?(name: String, age: Int){ if name.isEmpty || age < 0 { return nil } self.name = name self.age = age } } // 에러! nil을 리턴할 수 있기 때문에, Person? Person! 으로 타입을 지정하거나 ??로 Nil-coalescing을 해주어야 한다 // let yagom: Person = Person(name: "yagom", age: 30) let yagom: Person? = Person(name: "yagom", age: 30) // optional binding. non-optional 변수인 person에 optional 변수인 yagom을 할당할 수 있다면 {}안의 내용을 실행하라 if let person: Person = yagom { print(person.name) }else{ print("Person wasn't initialized") } // print yagom let chope: Person? = Person(name: "chope", age: -10) if let person: Person = chope { print(person.name) }else{ print("Person wasn't initialized") } // print Person wasn't initlaized
11.1.8 함수를 사용한 프로퍼티 기본값 설정
// 11.1.8 함수를 사용한 프로퍼티 기본값 설정 // 사용자 정의 연산을 통해 저장 프로퍼티 기본값을 설정하고자 한다면 클로저나 함수를 사용하여 프로퍼티 기본값을 제공할 수 있음 // 클로저를 사용한다면, 클로저가 실행되는 시점은 초기화할 때 인스턴스의 다른 프로퍼티 값이 설정되는 것이라는 것을 명심해야 함 // 즉, 클로저 내부에서는 인스턴스의 다른 프로퍼티를 사용할 수 없음. 다른 프로퍼티에 기본값이 있다고 해도 안 됨 import Foundation struct Student { var name: String? var age: Int? } class SchoolClass { var students: [Student] = { // 새로운 인스턴스를 생성하고 사용자 정의 연산을 통한 후 반환해 줌 // 반환되는 값의 타임은 [Student] 타입이어야 함 var arr: [Student] = [Student]() for num in 1...15 { var student: Student = Student(name: nil, age: nil) arr.append(student) } return arr }() // 클로저 뒤에 소괄호가 붙는 이유는 클로저를 실행하기 위함 // 클로저를 실행한 결과값이 프로퍼티의 기본값이 됨 // 소괄호가 없다면 프로퍼티의 기본값은 클로저 그 자체가 됨 } let myClass: SchoolClass = SchoolClass() print(myClass.students.count)
11.2 인스턴스 소멸
// 11.2 인스턴스 소멸 // Deinitlizer // Deinitlizer는 initalizer와 반대 역할을 함. 메모리에서 해제되기 직전 클래스의 인스턴스와 관련하여 원하는 정리 작업을 구현할 수 있음 // 클래스의 인스턴스가 메모리에서 소멸되기 직전에 호출됨 // deinit 키워드 사용 // 클래스의 인스턴스에만 구현할 수 있음 // 스위프트는 인스턴스가 더 이상 필요하지 않으면 자동으로 메모리에서 소멸시킴. // 인스턴스 대부분은 소멸시킬 때 디이니셜라이저를 사용해 별도의 메모리 관리 작업을 할 필요가 없음 // 하지만 예를 들어 인스턴스 내부에서 파일을 불러와 여는 등 외부 자원을 사용했다면 인스턴스를 소멸하기 전에 파일을 다시 저장하고 닫는 등의 작업이 필요. // 이 경우 디이니셜라이저 사용 가능 // 디이니셜라이저는 단 하나만 구현할 수 있으며, 자동으로 호출되기 때문에 별도의 코드로 호출할 수 없음 class FileManager { var fileName: String init(fileName: String) { self.fileName = fileName } func openFile() { print("Open File: \(self.fileName)") } func modifyFile() { print("modify file: \(self.fileName)") } func closeFile() { print("close file: \(self.fileName)") } deinit { print("Deinit instance") self.closeFile() } } var fileManager: FileManager? = FileManager(fileName: "abc.txt") if let manager: FileManager = fileManager{ manager.openFile() manager.modifyFile() } fileManager = nil // Deinit instance