Swift/문법 및 이론

Swift : 기초문법 [인스턴스 #3 초기화 위임, 실패가능한 이니셜라이저]

서근 2022. 1. 15.
공지사항
사파리보다는크롬으로 접속을 권장합니다
사파리로 접속시 gif 파일이 표시되지 않을 수 있습니다

본 게시글은 yagom님의 Swift 프로그래밍 3판을 참고하여 작성되었습니다.

 

초기화 위임

값 타입인 구조체와 열거형은 코드 중복을 피하기 위해서 한 이니셜라이저가 다른 이니셜라이저에게 일부 초기화를 위함하는 초기화 위임을 간단하게 구현 가능하다. 하지만, 참조 타입인 클래스는 불가능 하니 주의!

 

구조체와 열거형에서 이니셜라이저가 다른 이니셜라이저를 호출하려면 self.init 키워드를 사용한다. 그리고 반드시 이니셜라이저 안에서만 사용 가능하고, 이것을 사용한다는 것은 사용자 정의 이니셜라이저를 정의하고 있다는 뜻이 된다.

 

하지만 저번 게시글에서 사용자 정의 이니셜라이저를 정의하면 기본 이니셜라이저와 멤버와이즈 이니셜라이저를 사용할 수 없다고 했는데, 초기화를 위임 하기 위해서는 최소 두 개 이상의 사용자 정의 이니셜라이저를 정의해야 한다.

enum Student {
    case elementary, middle, heigh, adult
    case none
    
    //사용자 정의 이니셜라이저가 있으면, init() 메서드를 구현해줘야 기본 이니셜라이저 사용가능
    init() {
        self = .none
    }
    
    // 사용자 이니셜 라이저 1
    init(age KoreanAge: Int) {
        switch KoreanAge {
        case 8...13:
            self = .elementary
        case 14...16:
            self = .middle
        case 17...19:
            self = .heigh
        case 20...:
            self = .adult
        default:
            self = .none
        }
    }
    
    // 사용자 이니셜라이저 2
    init(bornAt: Int, currentYear: Int) {
        self.init(age: currentYear - bornAt + 1) //첫번째 이니셜라이저를 호출함 (초기화 위임)
    }
}

var Seogun: Student = Student(age: 27) //adult
Seogun = Student(bornAt: 2012, currentYear: 2022) //elementary

위 코드를 살펴보자면, 두 개의 사용자 정의 이니셜라이저가 있고, 첫 번째 이니셜라이저는 나이게 맞게 구분되어있어서 이니셜라이저를 초기화 하고, 두 번째 사용자는 태어난 년도와 현재 년도를 전달 받아 나이를 계산한 뒤에 첫 번째 이니셜라이저에게 초기화 위임을 한다.

 

이렇게 해서 코드를 중복으로 사용하지 않고 효율적으로 여러 case의 이니셜라이저를 만들 수 있게 된다.

실패 가능한 이니셜라이저

이니셜라이저를 통해 인스턴스를 초기화할 수 없는 몇가지 예외 상황이 있는데, 대표적으로 이니셜라이저의 전달인자로 잘못된 값이나 적절치 못한 값이 전달 되었을 때 이니셜라이저는 인스턴스 초기화에 실패할 수 있다. 

 

이니셜라이저를 정의할 때 이러한 실패 가능성을 염두에 두기도 한다. 이런 실패 가능성을 가진 이니셜라이저를 실패 가능한 이니셜라이저 Failable initializer이라고 부른다.

 

실패 가능한 이니셜라이저는 클래스, 구조체, 열거형 모두에서 정의할 수 있고, 실패 했을 때 nil을 반환 해주므로 반환 타입은 당연히 옵셔널로 지정된다. 

 

따라서 실패 가능한 이니셜라이저는 init 이 아닌 init? 키워드를 사용하게 된다.

TIP
 
 

이니셜라이저 매개변수
실패하지 않은 매개변수와 실패 가능한 이니셜라이저는 똑같은 이름의 매개변수 타입을 가질수 없다.

실패 가능한 이니셜라이저는 특정 값을 반환하지 않기 때문에 초기화가 실패하면 return nil 반환, 반대로 성공하면 return을 할당해 초기화의 성공과 실패만을 표현할 뿐, 실제 값을 반환하지는 않는다.

값이 잘못 전달되면 인스턴스 초기화에 실패할 수 있는데, 실패 가능한 이니셜라이저를 사용하면 잘못된 전달인자 전달받았을 때 초기화하지 않을 수 있다.

class Person {
    let name: String
    var age: Int?
    
    init?(name: String) {
        if name.isEmpty {
            return nil
        } else {
            self.name = name
        }
    }
    
    init?(name: String, age: Int) {
        if name.isEmpty || age < 0 {
            return nil
        } else {
            self.name = name
            self.age = age
        }
    }
}

let Seogun: Person? = Person(name: "서근")

if let person: Person = Seogun {
    print(person.name)
} else {
    print("Person class 초기화에 실패했습니다")
} //서근


let Mijin: Person? = Person(name: "미진", age: -20)

if let person: Person = Mijin {
    print(person.name)
} else {
    print("Person class 초기화에 실패했습니다")
} //Person class 초기화에 실패했습니다


let Cheolsu: Person? = Person(name: "", age: 10)

if let person: Person = Cheolsu {
    print(person.name)
} else {
    print("Person class 초기화에 실패했습니다")
} //Person class 초기화에 실패했습니다

실패 가능한 이니셜라이저는 클래스와 구조체에서도 유용하게 사용되지만,

특히 열거형에서 더 유용하게 사용된다. 특정 case에 맞지 않는 값이 들어오게 된다면 생성에 실패할 수 있다. 또는 rawValue로 초기화할 때, 잘못된 rawValue가 전달된다면 열거형 인스턴스를 생성하지 못한다. 따라서rawValue를 통한 이니셜라이저는 기본적으로 실패 가능한 이니셜라이저로 제공된다.

enum Student: String {
    case elementary = "초등학생", middle = "중학생", heigh = "고등학생", adult = "성인"
  
    init?(age KoreanAge: Int) {
        switch KoreanAge {
        case 8...13:
            self = .elementary
        case 14...16:
            self = .middle
        case 17...19:
            self = .heigh
        case 20...:
            self = .adult
        default:
            return nil
        }
    }
    
    init?(bornAt: Int, currentYear: Int) {
        self.init(age: currentYear - bornAt + 1)
    }
}

var Seogun: Student? = Student(age: 10) // 초등학생
Seogun = Student(bornAt: 2021, currentYear: 2022) // nil
Seogun = Student(rawValue: "유치원생") //nil
Seogun = Student(rawValue: "중학생") //middle

 

 

읽어주셔서 감사합니다 🤟

 

 

 

728x90
  • 이전글
  • 다음글