일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 두근두근 자료구조
- 미로 탐색 알고리즘
- 백준
- 자료구조
- 시간복잡도
- windosw 문자열
- War Game
- 파이썬
- HTML
- Java
- 정렬 알고리즘
- Stack
- 스택
- c언어
- PHP
- C
- web
- pwnable.kr
- SWiFT
- level13
- ftz
- LoB
- System
- 큐
- windosws wbcs
- ftz level13
- 재귀
- 파일 시스템
- OSI
- 암호수학
- Today
- Total
나의 기록, 현진록
[Swift] 11.1.5 기본 이니셜라이저와 멤버와이즈 이니셜라이저 / 초기화 위임 / 실패 가능한 이니셜라이저 본문
[Swift] 11.1.5 기본 이니셜라이저와 멤버와이즈 이니셜라이저 / 초기화 위임 / 실패 가능한 이니셜라이저
guswlsdk 2021. 7. 26. 11:36스위프트 프로그래밍 3판 - 야곰 지음
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: 0, y: 0)
let size: Size = Size(width: 50.0, height: 50.5)
//구조체의 저장 프로퍼티에 기본값이 있는 경우
//필요한 매개변수만 사용하여 초기화할 수도 있습니다.
let somePoint: Point = Point()
let someSize: Size = Size(width: 50)
let anotherPoint: Point = Point(y: 100)
11.1.6 초기화 위임
값 타입인 구조체와 열거형은 코드의 중복을 피하기 위하여 이니셜라이저가 다른 이니셜라이저에게 일부 초기화를 위임하는 초기화 위임을 간단하게 구현할 수 있습니다. 하지만 클래스는 상속을 지원하는 터라 간단한 초기화 위임도 할 수 없습니다.
값 타입에서 이니셜라이저가 다른 이니셜라이저를 호출하려면 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)
print(younger)
younger = Student(bornAt: 1998, currentYear: 2016)
print(younger)
열거형은 두 개의 사용자 정의 이니셜라이저가 있습니다. 첫 번째 사용자 정의 이니셜라이저는 나이를 전달받아 나이에 맞는 학교를 case로 구분한 이니셜라이저를 초기화하고, 두 번째 사용자 정의 이니셜라이저는 태어난 해와 현재 연도를 전달받아 나이로 계산한 후 첫 번째 이니셜라이저에 초기화를 위임합니다. 이렇게 초기화 위임 방법을 사용하면 코드를 중복으로 쓰지 않고도 효율적으로 여러 case의 이니셜라이저를 만들 수 있습니다.
11.1.7 실패 가능한 이니셜라이저
이니셜라이저를 통해 인스턴스를 초기화할 수 없는 여러 가지 예외 상황이 있습니다. 대표적으로 이니셜라이저의 전달인자로 잘못된 값이나 적절치 못한 값이 전달되었을 때, 이니셜라이저는 인스턴스를 초기화에 실패할 수 있습니다. 그 외에도 여러 이유로 인스턴스 초기화에 실패할 수 있습니다.
이니셜라이저를 정의할 때 이런 실패 가능성을 염두에 두기도 하는데, 이렇게 실패 가능성을 내포한 이니셜라이저를 실패 가능한 이니셜라이저라고 부릅니다. 실패 가능한 이니셜라이저는 클래스, 구조체, 열거형 등에 모두 정의할 수 있습니다. 실패 가능한 이니셜라이저는 실패 했을 때 nil을 반환해주므로 반환 타입이 옵셔널로 지정됩니다. 따라서 실패 가능한 이니셜라이저는 init 대신에 init? 키워드를 사용합니다.
이니셜라이저의 매개변수
실패하지 않는 이니셜라이저와 실패 가능한 이니셜라이저를 같은 이름과 같은 매개변수 타입을 갖도록 정의할 수 없습니다.
실패 가능한 이니셜라이저는 실제 특정 값을 반환하지 않습니다. 초기화를 실패했을 때는 return nil을. 반대로 초기화에 성공했을 때는 return을 적어 성공과 실패를 표현할 뿐 실제 값을 반환하지는 않습니다.
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
}
}
let hyunjin: Person? = Person(name: "hyunjin", age: 11)
if let person: Person = hyunjin{
print(person.name)
}else{
print("Person wasn't initialized")
}
let chope: Person? = Person(name: "chope", age: -10)
if let person: Person = chope{
print(person.name)
}else{
print("Person wasn't initialized")
}
let eric: Person? = Person(name: "", age: 30)
if let person: Person = eric{
print(person.name)
}else{
print("Person wasn't initialized")
}
실패 가능한 이니셜라이저는 구조체와 클래스에서도 유용하지만 특히 열거형에서 유용하게 사용할 수 있습니다. 특정 case에 맞지 않는 값이 들어오면 생성에 실패할 수 있습니다. 혹은 rawValue로 초기화할 때, 잘못된 rawValue가 전달되어 들어온다면 열거형 인스턴스를 생성하지 못할 수 있습니다. 따라서 rawValue를 통한 이니셜라이저는 기본적으로 실패 가능한 이니셜로 제공합니다.
enum Student: String{
case elementary = "초등학생", middle = "중학생", high = "고등학생"
init?(koreanAge: Int){ //첫 번째 사용자 정의 이니셜라이저
switch koreanAge{
case 8...13:
self = .elementary
case 14...16:
self = .middle
case 17...19:
self = .high
default:
return nil
}
}
init?(bornAt: Int, currentYear: Int){//두 번째 사용자 정의 이니셜라이저
self.init(koreanAge: currentYear - bornAt + 1)
}
}
var younger: Student? = Student(koreanAge: 20)
print(younger)
younger = Student(bornAt: 2020, currentYear: 2016)
print(younger)
younger = Student(rawValue: "대학생")
print(younger)
younger = Student(rawValue: "고등학생")
print(younger)
11.1.8 함수를 사용한 프로퍼티 기본값 설정
만약 사용자 정의 연산을 통해 저장 프로퍼티 기본값을 설정하고자 한다면 클로저나 함수를 사용하여 프로퍼티 기본값을 제공할 수 있습니다. 인스턴스를 초기화할 때 함수나 클로저가 호출되면서 연산 결괏값을 프로퍼티 기본값 프로퍼티 기본값으로 제공해줍니다. 그렇기 때문에 클로저나 함수의 반환 타입은 프로퍼티의 타입과 일치해야 합니다.
만약 프로퍼티 기본값을 설정해주기 위해서 클로저를 사용한다면 클로저가 실행되는 시점은 초기화할 때 인스턴스의 다른 프로퍼티 값이 설정되기 전이라는 것도 명심해야 합니다. 즉, 클로저 내부에서는 인스턴스의 다른 프로퍼티를 사용하여 연산할 수 없다는 것입니다. 다른 프로퍼티에 기본값이 있다고 해도 안 됩니다. 또한, 클로저 내부에서 self 프로퍼티도 사용할 수 없으며, 인스턴스 메서드를 호출할 수도 없습니다.
class SomeClass{
let someProperty: SomeType = {
//새로운 인스턴스를 생성하고 사용자 정의 연산을 통한 후 반환해줍니다.
//반환되는 값의 타입은 SomeType과 같은 타입이어야 합니다.
return someValue
}()
}
클로저 뒤에 소괄호가 붙은 이유는 클로저를 실행하기 위해서입니다. 클로저 뒤에 소괄호가 붙어 클로저를 실행한 결괏값은 프로퍼티의 기본값이 됩니다. 만약 소괄호가 없다면 프로퍼티의 기본값은 클로저 그 자체가 됩니다. 우리가 의도했던 것과는 전혀 다른 의미가 되는 것이죠.
struct Student{
var name: String?
var number: Int?
}
class SchoolClass{
var students: [Student] = {
//새로운 인스턴스를 생성하고 사용자 정의 연산을 통한 후 반환해줍니다.
//반환되는 값의 타입은 [Student] 타입이어야 합니다.
var arr: [Student] = [Student]()
for num in 1...15{
var student: Student = Student(name: nil, number: num)
arr.append(student)
}
return arr
}()
}
let myClass: SchoolClass = SchoolClass()
print(myClass.students.count)
'Programming > Swift' 카테고리의 다른 글
[Swift] 13. 클로저 / 기본 클로저 / 후행 클로저 (0) | 2021.07.27 |
---|---|
[Swift] 11.2 인스턴스 소멸 (0) | 2021.07.26 |
[Swift] 11.1 인스턴스 생성 / 이니셜라이저 / 옵셔널 프로퍼티 / 상수 프로퍼티 (0) | 2021.07.23 |
[Swift] 10.2 메서드 / 인스턴스 메서드 / 타입 메서드 (0) | 2021.07.20 |
[Swift] 타입 프로퍼티 (0) | 2021.07.19 |