728x90
클래스 타입에 대한 초기화 구문위임(Initializer Delegation for Class Types)
- 지정된 초기화 구문과 편의 초기화 구문 사이의 관계를 단순화 하기위해 Swift는 초기화 사이의 위임 호출에 대한 3가지 규칙 적용
- 지정된 초기화 구문은 상위 클래스로부터 지정된 초기화 구문을 호출
- 편의 초기화 구문은 같은 클래스로부터 다른 초기화 구문을 호출
- 편의 초기화 구문은 궁극적으로 지정된 초기화 구문을 호출
- 지정 초기화 구문은 항상 위로 위임
- 편의 초기화 구문은 항상 옆으로 위임
2단계 초기화(Two-Phase Initialization)
- Swift에서 클래스 초기화는 2단계 프로세스, 첫번째 단계에서 각 저장된 프로퍼티가 해당 프로퍼티를 도입한 클래스에 의해 초기값이 할당
- 저장된 프로퍼티에 초기 상태가 결정되면 두번쨰 단계 실행
- 새 인스턴스가 사용할 준비가 된 것으로 간주하기전에 각 클래스에 저장된 프로퍼티를 추가로 사용자화
- 장점
- 초기화를 안전하게 수행하는 동시에 유연성 제공
- 프로퍼티 값이 초기화 되기전에 접근하는 것을 막고 다른 초기화 구문이 다른 값을 설정하는 것을 막음
- swift의 컴파일러는 에러없이 2단계 초기화가 되었는지 4가지 검사 실행
- 클래스에 의해 도입된 모든 포로퍼티가 초기화 되었는지 확인
- 상속된 프로퍼티에 값을 할당하기전에 상위 클래스 초기화 구문 위임
- 편의 초기화 구문은 모든 프로퍼ㅣ에 값을 할당하기전에 다른 초기화 구문에 위임
- 첫번째 초기화가 완료될 때까지 인스턴스 메서드 호출or 인스턴스 프로퍼티 값 읽거나 self로 참조
초기화 구문 상속과 재정의(Initializer Inheritance and Overriding)
- 하위 클래스는 기본적으로 상위 클래스 초기화 구문(init)을 상속 ❌
- 상위 클래스의 간단한 초기화 구문이 더 특별한 일을 위한 하위 클래스에 상속되고 초기화 되지않은 하위 클래스의 새로운 인스턴스 생성하기 위해 사용되는 상황 방지
- 사용자 정의 하위 클래스가 상위 클래스와 동일한 초기화 구문 중 하나 이상을 표시하려면 하위 클래스 내에서 해당 초기화 구문의 사용자 정의 구현을 제공
- 상위 클래스에 지정된 초기화 구문과 일치하는 하위 클래스 초기화 구문을 작성할 때 지정된 초기화 구문의 재정의를 효과적으로 제공
- 하위 클래스의 초기화 구문 정의 전에 override 수식어를 작성
- 반대로 상위 클래스에 편의 초기화 구문과 일치하는 하위 클래스 초기화 구문을 작성할 경우 상위 클래스 편의 초기화 구문은 하위 클래스에서 직접적으로 호출될 수 없음
// 지정된 초기화 구문, 편의 초기화 구문
class Bulmang {
var age = 24
var description: String {
return "His age is \\(age)"
}
}
let bulmang = Bulmang()
print(bulmang.description)
// RealAge 하위 클래스는 init()인 지정된 초기화 구문을 정의
// Bicycle의 상위 클래스에 지정된 초기화 구문과 일치 override가 표시
class RealAge: Bulmang{
override init() {
super.init()
age = 25
}
}
let realAge = RealAge()
print(realAge.description)
// His age is 24
// His age is 25
class Job: Bulmang{
var job: String
init(job: String) {
self.job = job
// super..init()은 암시적으로 호출됨
}
override var description: String {
return "\\(super.description) and he is \\(job)"
}
}
let job = Job(job: "Apple Developer Academy Student")
print("\\(job.description)")
// His age is 24 and he is Apple Developer Academy Student
자동 초기화 구문 상속(Automatic Initializer Inheritance)
- 하위 클래스는 기본적으로 상우 클래스 초기화구문을 상속❌
- 특정 조건 충족시 상위클래스 초기화 구문으로 자동 상속
- 하위 클래스가 지정된 초기화 구문을 정의❌ → 자동으로 초기화 구문 상속
- 하위 클래스가 규칙 1에 따라 상속하거나 정의의 부분으로 사용자 정의 구현을 제공하여 모든 상위 클래스 지정된 초기화 구문의 구현을 제공하면 모든 상위 클래스 편의 초기화 구문을 자동으로 상속
지정된 초기화 구문과 편의 초기화 구문 동작 (Designated and Convenience Initializers in Action)
// 지정된 초기화 구문, 편의 초기화 구문, 자동 상속 초기화 구문을 보여줌
class Academy {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name:"[Unnamed]")
}
}
let academy = Academy(name: "AppleDeveloper")
let pohang = Academy()
class ADAP: Academy {
var member: Int
init(name: String,member: Int) {
self.member = member
super.init(name: name)
}
override convenience init(name: String){
self.init(name: name, member: 5)
}
}
실패 가능한 초기화 구문(Failable Initializers)
- 초기화가 실패할 수 있는 것에 대한 클래스, 구조체, 또는 열거형을 정의하는 것이 유용할 수 있음
- 유효하지 않은 초기화 파라미터 값, 필수 외부 리소스 부재 또는 초기화 성공을 방해하는 기타 다른조건에 의해 실패
- 실패할 수 있는 초기화 조건을 대처하려면 실패 가능한 초기화 구문을 클래스, 구조체, 또는 열거형 일부로 정의
- init 키워드 뒤에 물음표를 표기하여 실패 가능한 초기화 구문을 작성 (init?)
- 인스턴스 생성과정중 실패할 가능성이 있는 작업을 수행할 때 사용
- 주어진 인수 값이 유효 하지 않거나 외부 리소스를 사용할 수 없는 경우 포함
- nil을 반환 할 수 있고 외부에서 제공되는 데이터(API 호출, 파일 읽기, 사용자 입력 등) 데이터가 예상과 일치 하지 않을 수 도 있음
- 예외 처리와 안정성을 보장하는 데 유용한 기능
let wholeNumber: Double = 12345.0
let pi = 3.14159
// 숫자 타입 간의 변환이 값을 정확하게 유지하도록 하려면 init(exactly:) 초기화 구문을 사용
if let valueMaintained = Int(exactly: wholeNumber) {
print("\\(wholeNumber) conversion to Int maintains value of \\(valueMaintained)")
}
// Prints "12345.0 conversion to Int maintains value of 12345"
let valueChanged = Int(exactly: pi)
// valueChanged is of type Int?, not Int
if valueChanged == nil {
print("\\(pi) conversion to Int does not maintain value")
}
// Prints "3.14159 conversion to Int does not maintain value"
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \\(giraffe.species)")
}
// Prints "An animal was initialized with a species of Giraffe"
let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?, not Animal
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// Prints "The anonymous creature could not be initialized"
열거형을 위한 실패 가능한 초기화 구문(Failable Initializers for Enumerations)
- 하나 이상의 파라미터를 기반으로 적절한 열거형 케이스를 선택하기 위해 실패 가능한 초기화 구문을 사용
- 파라미터가 적절한 열거형 케이스와 일치하지 않으면 실패
// 3가지 가능한 상태에 대한 적절한 열거형 케이스를 선택하고 파라미터가 어떠한 상태도 일치하지 않으면
// 실패를 발생하기 위해 실패 가능한 초기화 구문을 사용
enum TemperatureUnit {
case kelvin, celsius, fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .kelvin
case "C":
self = .celsius
case "F":
self = .fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
원시값을 가진 열거형에 대한 실패 가능한 초기화 구문 (Failable Initializers for Enumerations with Raw Values)
- 원시값을 가진 열거형은 적절한 원시값 타입의 rawValue 라는 파라미터를 가지고 일치하는 값을 찾으면 일치하는 열거형 케이스를 선택하거나 일치하는 값이 없으면 초기화 실패를 나타내는 실패 가능한 초기화 구문을 자동으로 받음
//Character 타입의 원시값을 사용하고 init?(rawValue:) 초기화 구문의 장점을 가지는
// 위의 TemperatureUnit 예제를 재작성할 수 있음
enum TemperatureUnit: Character {
case kelvin = "K", celsius = "C", fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
초기화 실패 전파 (Propagation of Initialzation Failure)
- 클래스, 구조체, 또는 열거형의 실패 가능한 초기화 구문은 같은 클래스, 구조체, 또는 열거형에 다른 실패 가능한 초기화 구문으로 위임
- 하위 클래스 실패 가능한 초기화 구문은 상위 클래스 실패 가능한 초기화 구문으로 위임
- 두 경우 모두 초기화 실패를 유발하는 다른 초기화 구문에 위임하면 전체 초기화 프로세스는 즉시 실패하고 더이상 초기화 코드는 실행되지 않음
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
// quantity 가 유효하지 않으면 전체 초기화 프로세스는 즉시 실패하고 더이상 초기화 코드는 실행되지 않음
class CartItem: Product {
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity
super.init(name: name)
}
}
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \\(twoSocks.name), quantity: \\(twoSocks.quantity)")
}
// Prints "Item: sock, quantity: 2"
실패 가능한 초기화 구문 재정의 (Overriding a Failable Initializer)
- 다른 초기화 구문처럼 상위 클래스 실패 가능한 초기화 구문을 하위 클래스에 재정의 가능
- 하위 클래스 실패 불가능한 초기화구문으로 상위 클래스 실패 가능한 초기화 구문을 재정의
- 상위 클래스의 초기화가 실패하더라도 초기화를 실패할 수 없는 하위 클래스를 정의
- 실패 가능한 상위 클래스 초기화 구문을 실패 불가능한 하위 클래스 초기화 구문으로 재정의하면 상위 클래스 초기화 구문으로 위임하는 방법은 실패 가능한 상위 클래스 초기화 구문의 값을 강제로 언래핑 하는 것
// 비어 있지 않은 문자열 값이나 nil 은 가능하지만 빈 문자열은 불가능한 name 프로퍼티를 가지고 초기화 될 수 있는 문서를 모델링
class Document {
var name: String?
~~~~// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a nonempty name value
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
// AutomaticallyNamedDocument 하위 클래스는 Document 에 의해 도입된 지정된 초기화 구문 둘다 재정의
// 인스턴스가 이름 없이 초기화 되거나 빈 문자열이 init(name:) 초기화 구문에 전달되면 AutomaticallyNamedDocument 인스턴스는 초기 name 값이 "[Untitled]" 임을 보장
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
init! 실패 가능한 초기화 구문 (The init! Failable Initializer)
- 일반적으로 init 키워드 뒤에 물음표를 배치하여 (init?) 적절한 타입의 옵셔널 인스턴스를 생성하는 실패 가능한 초기화 구문을 정의.
- 적절한 타입의 암시적으로 언래핑된 옵셔널 인스턴스를 생성하는 실패 가능한 초기화 구문을 정의할 수 있습니다. 물음표 대신에 init 키워드 뒤에 느낌표를 위치 시키면 됨 (init!).
- init? 에서 init! 로 또는 그 반대로 위임할 수 있으며 init? 를 init! 로 또는 그 반대로 재정의 할 수 있움 init 에서 init! 으로 위임할 수도 있지만 그렇게 하면 init! 초기화 구문에 의해 초기화가 실패합니다.
필수 초기화 구문 (Required Initializers)
- 클래스 초기화 구문에 정의 앞에 required 수식어를 작성하여 클래스의 모든 하위 클래스가 해당 초기화 구문을 구현해야 함
class SomeClass {
required init() {
// initializer implementation goes here
}
}
// 초기화 구문 요구사항이 체인의 추가 하위 클래스에 적용됨을 나타내기 위해 필수 초기화 구문에 모든 하위 클래스 구현 전에 required 수식어를 작성
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
클로저 또는 함수를 사용하여 기본 프로퍼티 값 설정 (Setting a Default Property Value with a Closure or Function)
- 저장된 프로퍼티의 기본값에 일부 사용자 정의 또는 설정이 필요한 경우 해당 프로퍼티에 대한 사용자 정의된 기본값을 제공하기 위해 클로저 또는 전역 함수를 사용
- 프로퍼티가 속한 타입의 새로운 인스턴스가 초기화 될 때마다 클로저나 함수는 호출되고 그것의 반환값은 프로퍼티의 기본값으로 할당
- 클로저나 함수는 일반적으로 프로퍼티로 같은 타입의 임시값을 생성하고 원하는 초기상태를 나타내도록 해당 값을 조정한 다음 프로퍼티의 기본값으로 사용되기 위해 임시값을 반환
class SomeClass {
let someProperty: SomeType = {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
클래스 타입에 대한 초기화 구문위임(Initializer Delegation for Class Types)
- 지정된 초기화 구문과 편의 초기화 구문 사이의 관계를 단순화 하기위해 Swift는 초기화 사이의 위임 호출에 대한 3가지 규칙 적용
- 지정된 초기화 구문은 상위 클래스로부터 지정된 초기화 구문을 호출
- 편의 초기화 구문은 같은 클래스로부터 다른 초기화 구문을 호출
- 편의 초기화 구문은 궁극적으로 지정된 초기화 구문을 호출
- 지정 초기화 구문은 항상 위로 위임
- 편의 초기화 구문은 항상 옆으로 위임
2단계 초기화(Two-Phase Initialization)
- Swift에서 클래스 초기화는 2단계 프로세스, 첫번째 단계에서 각 저장된 프로퍼티가 해당 프로퍼티를 도입한 클래스에 의해 초기값이 할당
- 저장된 프로퍼티에 초기 상태가 결정되면 두번쨰 단계 실행
- 새 인스턴스가 사용할 준비가 된 것으로 간주하기전에 각 클래스에 저장된 프로퍼티를 추가로 사용자화
- 장점
- 초기화를 안전하게 수행하는 동시에 유연성 제공
- 프로퍼티 값이 초기화 되기전에 접근하는 것을 막고 다른 초기화 구문이 다른 값을 설정하는 것을 막음
- swift의 컴파일러는 에러없이 2단계 초기화가 되었는지 4가지 검사 실행
- 클래스에 의해 도입된 모든 포로퍼티가 초기화 되었는지 확인
- 상속된 프로퍼티에 값을 할당하기전에 상위 클래스 초기화 구문 위임
- 편의 초기화 구문은 모든 프로퍼ㅣ에 값을 할당하기전에 다른 초기화 구문에 위임
- 첫번째 초기화가 완료될 때까지 인스턴스 메서드 호출or 인스턴스 프로퍼티 값 읽거나 self로 참조
초기화 구문 상속과 재정의(Initializer Inheritance and Overriding)
- 하위 클래스는 기본적으로 상위 클래스 초기화 구문(init)을 상속 ❌
- 상위 클래스의 간단한 초기화 구문이 더 특별한 일을 위한 하위 클래스에 상속되고 초기화 되지않은 하위 클래스의 새로운 인스턴스 생성하기 위해 사용되는 상황 방지
- 사용자 정의 하위 클래스가 상위 클래스와 동일한 초기화 구문 중 하나 이상을 표시하려면 하위 클래스 내에서 해당 초기화 구문의 사용자 정의 구현을 제공
- 상위 클래스에 지정된 초기화 구문과 일치하는 하위 클래스 초기화 구문을 작성할 때 지정된 초기화 구문의 재정의를 효과적으로 제공
- 하위 클래스의 초기화 구문 정의 전에 override 수식어를 작성
- 반대로 상위 클래스에 편의 초기화 구문과 일치하는 하위 클래스 초기화 구문을 작성할 경우 상위 클래스 편의 초기화 구문은 하위 클래스에서 직접적으로 호출될 수 없음
// 지정된 초기화 구문, 편의 초기화 구문
class Bulmang {
var age = 24
var description: String {
return "His age is \\(age)"
}
}
let bulmang = Bulmang()
print(bulmang.description)
// RealAge 하위 클래스는 init()인 지정된 초기화 구문을 정의
// Bicycle의 상위 클래스에 지정된 초기화 구문과 일치 override가 표시
class RealAge: Bulmang{
override init() {
super.init()
age = 25
}
}
let realAge = RealAge()
print(realAge.description)
// His age is 24
// His age is 25
class Job: Bulmang{
var job: String
init(job: String) {
self.job = job
// super..init()은 암시적으로 호출됨
}
override var description: String {
return "\\(super.description) and he is \\(job)"
}
}
let job = Job(job: "Apple Developer Academy Student")
print("\\(job.description)")
// His age is 24 and he is Apple Developer Academy Student
자동 초기화 구문 상속(Automatic Initializer Inheritance)
- 하위 클래스는 기본적으로 상우 클래스 초기화구문을 상속❌
- 특정 조건 충족시 상위클래스 초기화 구문으로 자동 상속
- 하위 클래스가 지정된 초기화 구문을 정의❌ → 자동으로 초기화 구문 상속
- 하위 클래스가 규칙 1에 따라 상속하거나 정의의 부분으로 사용자 정의 구현을 제공하여 모든 상위 클래스 지정된 초기화 구문의 구현을 제공하면 모든 상위 클래스 편의 초기화 구문을 자동으로 상속
지정된 초기화 구문과 편의 초기화 구문 동작 (Designated and Convenience Initializers in Action)
// 지정된 초기화 구문, 편의 초기화 구문, 자동 상속 초기화 구문을 보여줌
class Academy {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name:"[Unnamed]")
}
}
let academy = Academy(name: "AppleDeveloper")
let pohang = Academy()
class ADAP: Academy {
var member: Int
init(name: String,member: Int) {
self.member = member
super.init(name: name)
}
override convenience init(name: String){
self.init(name: name, member: 5)
}
}
실패 가능한 초기화 구문(Failable Initializers)
- 초기화가 실패할 수 있는 것에 대한 클래스, 구조체, 또는 열거형을 정의하는 것이 유용할 수 있음
- 유효하지 않은 초기화 파라미터 값, 필수 외부 리소스 부재 또는 초기화 성공을 방해하는 기타 다른조건에 의해 실패
- 실패할 수 있는 초기화 조건을 대처하려면 실패 가능한 초기화 구문을 클래스, 구조체, 또는 열거형 일부로 정의
- init 키워드 뒤에 물음표를 표기하여 실패 가능한 초기화 구문을 작성 (init?)
- 인스턴스 생성과정중 실패할 가능성이 있는 작업을 수행할 때 사용
- 주어진 인수 값이 유효 하지 않거나 외부 리소스를 사용할 수 없는 경우 포함
- nil을 반환 할 수 있고 외부에서 제공되는 데이터(API 호출, 파일 읽기, 사용자 입력 등) 데이터가 예상과 일치 하지 않을 수 도 있음
- 예외 처리와 안정성을 보장하는 데 유용한 기능
let wholeNumber: Double = 12345.0
let pi = 3.14159
// 숫자 타입 간의 변환이 값을 정확하게 유지하도록 하려면 init(exactly:) 초기화 구문을 사용
if let valueMaintained = Int(exactly: wholeNumber) {
print("\\(wholeNumber) conversion to Int maintains value of \\(valueMaintained)")
}
// Prints "12345.0 conversion to Int maintains value of 12345"
let valueChanged = Int(exactly: pi)
// valueChanged is of type Int?, not Int
if valueChanged == nil {
print("\\(pi) conversion to Int does not maintain value")
}
// Prints "3.14159 conversion to Int does not maintain value"
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \\(giraffe.species)")
}
// Prints "An animal was initialized with a species of Giraffe"
let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?, not Animal
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// Prints "The anonymous creature could not be initialized"
열거형을 위한 실패 가능한 초기화 구문(Failable Initializers for Enumerations)
- 하나 이상의 파라미터를 기반으로 적절한 열거형 케이스를 선택하기 위해 실패 가능한 초기화 구문을 사용
- 파라미터가 적절한 열거형 케이스와 일치하지 않으면 실패
// 3가지 가능한 상태에 대한 적절한 열거형 케이스를 선택하고 파라미터가 어떠한 상태도 일치하지 않으면
// 실패를 발생하기 위해 실패 가능한 초기화 구문을 사용
enum TemperatureUnit {
case kelvin, celsius, fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .kelvin
case "C":
self = .celsius
case "F":
self = .fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
원시값을 가진 열거형에 대한 실패 가능한 초기화 구문 (Failable Initializers for Enumerations with Raw Values)
- 원시값을 가진 열거형은 적절한 원시값 타입의 rawValue 라는 파라미터를 가지고 일치하는 값을 찾으면 일치하는 열거형 케이스를 선택하거나 일치하는 값이 없으면 초기화 실패를 나타내는 실패 가능한 초기화 구문을 자동으로 받음
//Character 타입의 원시값을 사용하고 init?(rawValue:) 초기화 구문의 장점을 가지는
// 위의 TemperatureUnit 예제를 재작성할 수 있음
enum TemperatureUnit: Character {
case kelvin = "K", celsius = "C", fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
초기화 실패 전파 (Propagation of Initialzation Failure)
- 클래스, 구조체, 또는 열거형의 실패 가능한 초기화 구문은 같은 클래스, 구조체, 또는 열거형에 다른 실패 가능한 초기화 구문으로 위임
- 하위 클래스 실패 가능한 초기화 구문은 상위 클래스 실패 가능한 초기화 구문으로 위임
- 두 경우 모두 초기화 실패를 유발하는 다른 초기화 구문에 위임하면 전체 초기화 프로세스는 즉시 실패하고 더이상 초기화 코드는 실행되지 않음
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
// quantity 가 유효하지 않으면 전체 초기화 프로세스는 즉시 실패하고 더이상 초기화 코드는 실행되지 않음
class CartItem: Product {
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity
super.init(name: name)
}
}
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \\(twoSocks.name), quantity: \\(twoSocks.quantity)")
}
// Prints "Item: sock, quantity: 2"
실패 가능한 초기화 구문 재정의 (Overriding a Failable Initializer)
- 다른 초기화 구문처럼 상위 클래스 실패 가능한 초기화 구문을 하위 클래스에 재정의 가능
- 하위 클래스 실패 불가능한 초기화구문으로 상위 클래스 실패 가능한 초기화 구문을 재정의
- 상위 클래스의 초기화가 실패하더라도 초기화를 실패할 수 없는 하위 클래스를 정의
- 실패 가능한 상위 클래스 초기화 구문을 실패 불가능한 하위 클래스 초기화 구문으로 재정의하면 상위 클래스 초기화 구문으로 위임하는 방법은 실패 가능한 상위 클래스 초기화 구문의 값을 강제로 언래핑 하는 것
// 비어 있지 않은 문자열 값이나 nil 은 가능하지만 빈 문자열은 불가능한 name 프로퍼티를 가지고 초기화 될 수 있는 문서를 모델링
class Document {
var name: String?
~~~~// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a nonempty name value
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
// AutomaticallyNamedDocument 하위 클래스는 Document 에 의해 도입된 지정된 초기화 구문 둘다 재정의
// 인스턴스가 이름 없이 초기화 되거나 빈 문자열이 init(name:) 초기화 구문에 전달되면 AutomaticallyNamedDocument 인스턴스는 초기 name 값이 "[Untitled]" 임을 보장
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
init! 실패 가능한 초기화 구문 (The init! Failable Initializer)
- 일반적으로 init 키워드 뒤에 물음표를 배치하여 (init?) 적절한 타입의 옵셔널 인스턴스를 생성하는 실패 가능한 초기화 구문을 정의.
- 적절한 타입의 암시적으로 언래핑된 옵셔널 인스턴스를 생성하는 실패 가능한 초기화 구문을 정의할 수 있습니다. 물음표 대신에 init 키워드 뒤에 느낌표를 위치 시키면 됨 (init!).
- init? 에서 init! 로 또는 그 반대로 위임할 수 있으며 init? 를 init! 로 또는 그 반대로 재정의 할 수 있움 init 에서 init! 으로 위임할 수도 있지만 그렇게 하면 init! 초기화 구문에 의해 초기화가 실패합니다.
필수 초기화 구문 (Required Initializers)
- 클래스 초기화 구문에 정의 앞에 required 수식어를 작성하여 클래스의 모든 하위 클래스가 해당 초기화 구문을 구현해야 함
class SomeClass {
required init() {
// initializer implementation goes here
}
}
// 초기화 구문 요구사항이 체인의 추가 하위 클래스에 적용됨을 나타내기 위해 필수 초기화 구문에 모든 하위 클래스 구현 전에 required 수식어를 작성
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
클로저 또는 함수를 사용하여 기본 프로퍼티 값 설정 (Setting a Default Property Value with a Closure or Function)
- 저장된 프로퍼티의 기본값에 일부 사용자 정의 또는 설정이 필요한 경우 해당 프로퍼티에 대한 사용자 정의된 기본값을 제공하기 위해 클로저 또는 전역 함수를 사용
- 프로퍼티가 속한 타입의 새로운 인스턴스가 초기화 될 때마다 클로저나 함수는 호출되고 그것의 반환값은 프로퍼티의 기본값으로 할당
- 클로저나 함수는 일반적으로 프로퍼티로 같은 타입의 임시값을 생성하고 원하는 초기상태를 나타내도록 해당 값을 조정한 다음 프로퍼티의 기본값으로 사용되기 위해 임시값을 반환
class SomeClass {
let someProperty: SomeType = {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
클래스 타입에 대한 초기화 구문위임(Initializer Delegation for Class Types)
- 지정된 초기화 구문과 편의 초기화 구문 사이의 관계를 단순화 하기위해 Swift는 초기화 사이의 위임 호출에 대한 3가지 규칙 적용
- 지정된 초기화 구문은 상위 클래스로부터 지정된 초기화 구문을 호출
- 편의 초기화 구문은 같은 클래스로부터 다른 초기화 구문을 호출
- 편의 초기화 구문은 궁극적으로 지정된 초기화 구문을 호출
- 지정 초기화 구문은 항상 위로 위임
- 편의 초기화 구문은 항상 옆으로 위임
2단계 초기화(Two-Phase Initialization)
- Swift에서 클래스 초기화는 2단계 프로세스, 첫번째 단계에서 각 저장된 프로퍼티가 해당 프로퍼티를 도입한 클래스에 의해 초기값이 할당
- 저장된 프로퍼티에 초기 상태가 결정되면 두번쨰 단계 실행
- 새 인스턴스가 사용할 준비가 된 것으로 간주하기전에 각 클래스에 저장된 프로퍼티를 추가로 사용자화
- 장점
- 초기화를 안전하게 수행하는 동시에 유연성 제공
- 프로퍼티 값이 초기화 되기전에 접근하는 것을 막고 다른 초기화 구문이 다른 값을 설정하는 것을 막음
- swift의 컴파일러는 에러없이 2단계 초기화가 되었는지 4가지 검사 실행
- 클래스에 의해 도입된 모든 포로퍼티가 초기화 되었는지 확인
- 상속된 프로퍼티에 값을 할당하기전에 상위 클래스 초기화 구문 위임
- 편의 초기화 구문은 모든 프로퍼ㅣ에 값을 할당하기전에 다른 초기화 구문에 위임
- 첫번째 초기화가 완료될 때까지 인스턴스 메서드 호출or 인스턴스 프로퍼티 값 읽거나 self로 참조
초기화 구문 상속과 재정의(Initializer Inheritance and Overriding)
- 하위 클래스는 기본적으로 상위 클래스 초기화 구문(init)을 상속 ❌
- 상위 클래스의 간단한 초기화 구문이 더 특별한 일을 위한 하위 클래스에 상속되고 초기화 되지않은 하위 클래스의 새로운 인스턴스 생성하기 위해 사용되는 상황 방지
- 사용자 정의 하위 클래스가 상위 클래스와 동일한 초기화 구문 중 하나 이상을 표시하려면 하위 클래스 내에서 해당 초기화 구문의 사용자 정의 구현을 제공
- 상위 클래스에 지정된 초기화 구문과 일치하는 하위 클래스 초기화 구문을 작성할 때 지정된 초기화 구문의 재정의를 효과적으로 제공
- 하위 클래스의 초기화 구문 정의 전에 override 수식어를 작성
- 반대로 상위 클래스에 편의 초기화 구문과 일치하는 하위 클래스 초기화 구문을 작성할 경우 상위 클래스 편의 초기화 구문은 하위 클래스에서 직접적으로 호출될 수 없음
// 지정된 초기화 구문, 편의 초기화 구문
class Bulmang {
var age = 24
var description: String {
return "His age is \\(age)"
}
}
let bulmang = Bulmang()
print(bulmang.description)
// RealAge 하위 클래스는 init()인 지정된 초기화 구문을 정의
// Bicycle의 상위 클래스에 지정된 초기화 구문과 일치 override가 표시
class RealAge: Bulmang{
override init() {
super.init()
age = 25
}
}
let realAge = RealAge()
print(realAge.description)
// His age is 24
// His age is 25
class Job: Bulmang{
var job: String
init(job: String) {
self.job = job
// super..init()은 암시적으로 호출됨
}
override var description: String {
return "\\(super.description) and he is \\(job)"
}
}
let job = Job(job: "Apple Developer Academy Student")
print("\\(job.description)")
// His age is 24 and he is Apple Developer Academy Student
자동 초기화 구문 상속(Automatic Initializer Inheritance)
- 하위 클래스는 기본적으로 상우 클래스 초기화구문을 상속❌
- 특정 조건 충족시 상위클래스 초기화 구문으로 자동 상속
- 하위 클래스가 지정된 초기화 구문을 정의❌ → 자동으로 초기화 구문 상속
- 하위 클래스가 규칙 1에 따라 상속하거나 정의의 부분으로 사용자 정의 구현을 제공하여 모든 상위 클래스 지정된 초기화 구문의 구현을 제공하면 모든 상위 클래스 편의 초기화 구문을 자동으로 상속
지정된 초기화 구문과 편의 초기화 구문 동작 (Designated and Convenience Initializers in Action)
// 지정된 초기화 구문, 편의 초기화 구문, 자동 상속 초기화 구문을 보여줌
class Academy {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name:"[Unnamed]")
}
}
let academy = Academy(name: "AppleDeveloper")
let pohang = Academy()
class ADAP: Academy {
var member: Int
init(name: String,member: Int) {
self.member = member
super.init(name: name)
}
override convenience init(name: String){
self.init(name: name, member: 5)
}
}
실패 가능한 초기화 구문(Failable Initializers)
- 초기화가 실패할 수 있는 것에 대한 클래스, 구조체, 또는 열거형을 정의하는 것이 유용할 수 있음
- 유효하지 않은 초기화 파라미터 값, 필수 외부 리소스 부재 또는 초기화 성공을 방해하는 기타 다른조건에 의해 실패
- 실패할 수 있는 초기화 조건을 대처하려면 실패 가능한 초기화 구문을 클래스, 구조체, 또는 열거형 일부로 정의
- init 키워드 뒤에 물음표를 표기하여 실패 가능한 초기화 구문을 작성 (init?)
- 인스턴스 생성과정중 실패할 가능성이 있는 작업을 수행할 때 사용
- 주어진 인수 값이 유효 하지 않거나 외부 리소스를 사용할 수 없는 경우 포함
- nil을 반환 할 수 있고 외부에서 제공되는 데이터(API 호출, 파일 읽기, 사용자 입력 등) 데이터가 예상과 일치 하지 않을 수 도 있음
- 예외 처리와 안정성을 보장하는 데 유용한 기능
let wholeNumber: Double = 12345.0
let pi = 3.14159
// 숫자 타입 간의 변환이 값을 정확하게 유지하도록 하려면 init(exactly:) 초기화 구문을 사용
if let valueMaintained = Int(exactly: wholeNumber) {
print("\\(wholeNumber) conversion to Int maintains value of \\(valueMaintained)")
}
// Prints "12345.0 conversion to Int maintains value of 12345"
let valueChanged = Int(exactly: pi)
// valueChanged is of type Int?, not Int
if valueChanged == nil {
print("\\(pi) conversion to Int does not maintain value")
}
// Prints "3.14159 conversion to Int does not maintain value"
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \\(giraffe.species)")
}
// Prints "An animal was initialized with a species of Giraffe"
let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?, not Animal
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// Prints "The anonymous creature could not be initialized"
열거형을 위한 실패 가능한 초기화 구문(Failable Initializers for Enumerations)
- 하나 이상의 파라미터를 기반으로 적절한 열거형 케이스를 선택하기 위해 실패 가능한 초기화 구문을 사용
- 파라미터가 적절한 열거형 케이스와 일치하지 않으면 실패
// 3가지 가능한 상태에 대한 적절한 열거형 케이스를 선택하고 파라미터가 어떠한 상태도 일치하지 않으면
// 실패를 발생하기 위해 실패 가능한 초기화 구문을 사용
enum TemperatureUnit {
case kelvin, celsius, fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .kelvin
case "C":
self = .celsius
case "F":
self = .fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
원시값을 가진 열거형에 대한 실패 가능한 초기화 구문 (Failable Initializers for Enumerations with Raw Values)
- 원시값을 가진 열거형은 적절한 원시값 타입의 rawValue 라는 파라미터를 가지고 일치하는 값을 찾으면 일치하는 열거형 케이스를 선택하거나 일치하는 값이 없으면 초기화 실패를 나타내는 실패 가능한 초기화 구문을 자동으로 받음
//Character 타입의 원시값을 사용하고 init?(rawValue:) 초기화 구문의 장점을 가지는
// 위의 TemperatureUnit 예제를 재작성할 수 있음
enum TemperatureUnit: Character {
case kelvin = "K", celsius = "C", fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
초기화 실패 전파 (Propagation of Initialzation Failure)
- 클래스, 구조체, 또는 열거형의 실패 가능한 초기화 구문은 같은 클래스, 구조체, 또는 열거형에 다른 실패 가능한 초기화 구문으로 위임
- 하위 클래스 실패 가능한 초기화 구문은 상위 클래스 실패 가능한 초기화 구문으로 위임
- 두 경우 모두 초기화 실패를 유발하는 다른 초기화 구문에 위임하면 전체 초기화 프로세스는 즉시 실패하고 더이상 초기화 코드는 실행되지 않음
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
// quantity 가 유효하지 않으면 전체 초기화 프로세스는 즉시 실패하고 더이상 초기화 코드는 실행되지 않음
class CartItem: Product {
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity
super.init(name: name)
}
}
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \\(twoSocks.name), quantity: \\(twoSocks.quantity)")
}
// Prints "Item: sock, quantity: 2"
실패 가능한 초기화 구문 재정의 (Overriding a Failable Initializer)
- 다른 초기화 구문처럼 상위 클래스 실패 가능한 초기화 구문을 하위 클래스에 재정의 가능
- 하위 클래스 실패 불가능한 초기화구문으로 상위 클래스 실패 가능한 초기화 구문을 재정의
- 상위 클래스의 초기화가 실패하더라도 초기화를 실패할 수 없는 하위 클래스를 정의
- 실패 가능한 상위 클래스 초기화 구문을 실패 불가능한 하위 클래스 초기화 구문으로 재정의하면 상위 클래스 초기화 구문으로 위임하는 방법은 실패 가능한 상위 클래스 초기화 구문의 값을 강제로 언래핑 하는 것
// 비어 있지 않은 문자열 값이나 nil 은 가능하지만 빈 문자열은 불가능한 name 프로퍼티를 가지고 초기화 될 수 있는 문서를 모델링
class Document {
var name: String?
~~~~// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a nonempty name value
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
// AutomaticallyNamedDocument 하위 클래스는 Document 에 의해 도입된 지정된 초기화 구문 둘다 재정의
// 인스턴스가 이름 없이 초기화 되거나 빈 문자열이 init(name:) 초기화 구문에 전달되면 AutomaticallyNamedDocument 인스턴스는 초기 name 값이 "[Untitled]" 임을 보장
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
init! 실패 가능한 초기화 구문 (The init! Failable Initializer)
- 일반적으로 init 키워드 뒤에 물음표를 배치하여 (init?) 적절한 타입의 옵셔널 인스턴스를 생성하는 실패 가능한 초기화 구문을 정의.
- 적절한 타입의 암시적으로 언래핑된 옵셔널 인스턴스를 생성하는 실패 가능한 초기화 구문을 정의할 수 있습니다. 물음표 대신에 init 키워드 뒤에 느낌표를 위치 시키면 됨 (init!).
- init? 에서 init! 로 또는 그 반대로 위임할 수 있으며 init? 를 init! 로 또는 그 반대로 재정의 할 수 있움 init 에서 init! 으로 위임할 수도 있지만 그렇게 하면 init! 초기화 구문에 의해 초기화가 실패합니다.
필수 초기화 구문 (Required Initializers)
- 클래스 초기화 구문에 정의 앞에 required 수식어를 작성하여 클래스의 모든 하위 클래스가 해당 초기화 구문을 구현해야 함
class SomeClass {
required init() {
// initializer implementation goes here
}
}
// 초기화 구문 요구사항이 체인의 추가 하위 클래스에 적용됨을 나타내기 위해 필수 초기화 구문에 모든 하위 클래스 구현 전에 required 수식어를 작성
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
클로저 또는 함수를 사용하여 기본 프로퍼티 값 설정 (Setting a Default Property Value with a Closure or Function)
- 저장된 프로퍼티의 기본값에 일부 사용자 정의 또는 설정이 필요한 경우 해당 프로퍼티에 대한 사용자 정의된 기본값을 제공하기 위해 클로저 또는 전역 함수를 사용
- 프로퍼티가 속한 타입의 새로운 인스턴스가 초기화 될 때마다 클로저나 함수는 호출되고 그것의 반환값은 프로퍼티의 기본값으로 할당
- 클로저나 함수는 일반적으로 프로퍼티로 같은 타입의 임시값을 생성하고 원하는 초기상태를 나타내도록 해당 값을 조정한 다음 프로퍼티의 기본값으로 사용되기 위해 임시값을 반환
class SomeClass {
let someProperty: SomeType = {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
728x90
'기타 > Today I Learned' 카테고리의 다른 글
[TIL] Swift 문법 옵셔널 체이닝(Optional Chaining) (1) | 2023.05.17 |
---|---|
[TIL] Swift 문법 초기화(Initialization) - 1 (0) | 2023.05.10 |
[TIL] Swift 문법 상속(Inheritance) (0) | 2023.05.05 |
[TIL] Swift 문법(Subscripts) (0) | 2023.05.03 |
[TIL] Swift 문법 메소드(Methods) (0) | 2023.04.28 |