Swift) Initialization

Havi·2020년 11월 4일
0

Swift기초

목록 보기
12/19
post-custom-banner

Initialization

Initialization은 class, structure, enumeration의 인스턴스를 준비하는 프로세스이다.
이 인스턴스의 stored property에 대한 초기값을 설정하고, 새 인스턴스를 사용할 준비가 되기 전에 필요한 다른 설정 및 초기화를 수행한다.

Initializer를 통해 새 인스턴스를 만들며 Objective-C와 달리 Swift의 initializer는 값을 반환하지 않는다. initializer의 역할은 새 인스턴스가 처음 사용되기 전에 올바르게 초기화되었는지 확인하는 것이다.

class type의 Instances는 deinitializer를 구현할 수 있다.

Stored Properties에 대한 Initial Value 세팅

class와 structure는 인스턴스가 생성될 때 무조건 Stored Properties에 대한 Initial Value를 가지고 있어야한다.

Initial Value는 initializer내에서 설정할 수도 있고, default property value를 할당할 수도 있다.

이때 property observer를 호출하지 않고 direct하게 세팅된다.

Initializer

  1. 초기화 메소드의 이름은 init으로 통일된다.
  2. 매개변수의 개수, 이름, 타입은 임의로 정의할 수 있다.
  3. 매개변수의 이름과 개수, 타입이 서로 다른 여러 개의 초기화 메소드를 정의할 수 있다.
  4. 정의된 초기화 메소드는 직접 호출되기도 하지만, 대부분 인스턴스 생성 시 간접적으로 호출된다.
struct Resolution {
    var width = 0
    var height = 0

    init(width: Int) {
        self.width = width
    }

    // init은 파라미터에 따라 오버로딩이 가능하다.
    init(height: Int) {
        self.height = height
    }

    init(width: Int, height: Int) {
        self.width = width
        self.height = height
    }
}

Initializer Delegation

상속받은 모든 부모의 init을 호출한다.

class Base {
    var base: Double

    init() {
        self.base = 0.0
        print("Base Init")
    }
}

class ExBase: Base {
    // 오버라이드 된 init 실행 후 부모의 init 실행
    // 부모의 init을 먼저 실행하려면 super.init()을 명시적으로 호출해야함
    override init() {
    	//super.init()
        print("ExBase Init")
    }
}

let ex = ExBase()
// ExBase Init
// Base Init

// super.init()이 있을 경우

// Base Init
// ExBase Init

Optional Property Types

struct Question {
    var text: String
    var respond: String?

    init(text: String) {
        self.text = text
    }

    init(text: String, respond: String) {
        self.text = text
        self.respond = respond
    }
}

var question = Question(text: "Optional?")

question.respond = "No"

print(question.respond) // Optional(No)

Default Initializer

class Shopping {
    var name: String?
    var quantity = 1
    var purchased = false
}

// 모든 프로퍼티의 초기값이 설정되어있고, 초기자가 정의되지 않았을 경우 기본 initializer 제공
let shop = Shopping()

struct Size {
    var width: Int
    var height = 0
}

// Class 와 달리 Struct는 프로퍼티의 기본 값이 없어도 Memberwise Initializer 제공
var size = Size(width: 4, height: 6)

Class Inheritance and Initialization

Designated Initializers

init(parameters) {
    statements
}

Convenience Initializers

convenience init(parameters) {
    statements
}
  1. Designed Init은 반드시 직계 superClass의 Designed Init을 호출해야 한다.
  2. Convenience Init은 반드시 같은 클래스의 다른 Initializer를 호출해야 한다.
  3. Convenience Init는 궁극적으로 Designed Init을 호출해야 한다.

더 복잡한 형태의 delegate Init

Two-Phase Initialization

1단계 : 각 stored property는 초기값으로 초기화 된다.
모든 stored property의 상태가 결정되면 두번째 단계가 시작된다.

2단계 : 새로운 인스턴스의 사용이 준비되었다고 알려주기 전, stored property를 커스터마이징한다.

Swift의 컴파일러는 2단계 초기화가 에러없이 끝나는 것을 보장하기 위해 4단계 안전 확인(safety-check)을 한다.

안전 확인 1단계 지정 초기자는 클래스 안에서 초기화를 superclass의 초기자에게 위임하기 전에 모든 프로퍼티를 초기화 해야 합니다. 위에서 언급한 것 처럼 메모리에서 객체는 모든 저장된 프로퍼티가 초기 상태를 갖어야만 완전히 초기화 된것으로 간주되기 때문에 이 규칙을 만족시키기 위해 지정 초기자는 반드시 다른 초기자로 넘기기 전에 소유하고있는 모든 프로퍼티를 초기화 해야 합니다.

안전 확인 2단계 지정 초기자는 반드시 상속된 값을 할당하기 전에 superclass의 초기자로 위임을 넘겨야 합니다. 그렇지 않으면 상속된 값이 superclass이 초기자에 의해 덮어 쓰여지게 됩니다.

안전 확인 3단계 편리한 초기자는 반드시 어떤 프로퍼티를 할당하기 전에 다른 초기자로 위임을 넘겨야 합니다. 만약 그렇지 않으면 편리한 초기자에 의해 할당된 값을 다른 클래스의 지정 초기자에 의해 덮어 쓰여지게 됩니다.

안전 확인 4단계 이니셜라이저는 초기화의 1단계가 끝나기 전에는 self의 값을 참조하거나 어떤 인스턴스 프로퍼티, 메소드 등을 호출하거나 읽을 수 없습니다.

Initializer Inheritance and Overriding

Swift에서는 기본적으로 subclass에서 superclass의 이니셜라이저를 상속하지 않습니다.

따라서 Automatic Initializer Inheritance에 의해 서브클래스가 지정 초기자를 정의하지 않으면 자동으로 슈퍼클래스의 모든 지정 초기자를 상속한다.
또한 서브클래스가 수퍼클래스의 지정초기자를 모두 구현한 경우 자동으로 슈퍼클래스의 convenience Initializer를 추가한다.

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
    convenience init() {
        self.init(name: "[Unnamed]")
    }
}

let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"

let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]"

서브 클래싱의 예

class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
    }
    override convenience init(name: String) {
        self.init(name: name, quantity: 1)
    }
}

이렇게 선언 가능

let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)

Failable Initializers

초기화 과정 중에 실패할 가능성이 있는 초기자를 init뒤에 물음표(?)를 사용해 실패가 가능한 초기자라고 표시할 수 있습니다.

struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}

let fail = Animal(species: "") //  nil
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 temp = TemperatureUnit(symbol: "C")! // prints celsius
let temp2 = TemperatureUnit(symbol: "A") // nil

Enum에서 raw value를 사용하는 초기자

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."

Required Initializers

모든 서브클래스에서 반드시 구현해야 하는 초기자에는 아래 예제와 같이 required 키워드를 붙여 줍니다.

필수초기자 표시를 해도 꼭 구현을 할 필요는 없습니다.

class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}

클로저나 함수를 이용해 기본 프로퍼티 값을 설정하기

struct Chessboard {
    let boardColors: [Bool] = {
        var temporaryBoard = [Bool]()
        var isBlack = false
        for i in 1...8 {
            for j in 1...8 {
                temporaryBoard.append(isBlack)
                isBlack = !isBlack
            }
            isBlack = !isBlack
        }
        return temporaryBoard
    }()
    func squareIsBlackAt(row: Int, column: Int) -> Bool {
        return boardColors[(row * 8) + column]
    }
}

let board = Chessboard()
print(board.squareIsBlackAt(row: 0, column: 1))
// Prints "true"
print(board.squareIsBlackAt(row: 7, column: 7))
// Prints "false"
profile
iOS Developer
post-custom-banner

0개의 댓글