티스토리 뷰

출처

http://kka7.tistory.com/19



초기화

클래스, 구조체, 열거형의 인스턴스를 
사용하기 전에 준비하는 단계이다.

Swift 초기화는 Objective-C 와 다르게 값을 반환하지 않는다.
이 규칙은 타입의 새로운 인스턴스를 처음 사용하기 전에
확실히 초기화가 되는 것을 보장한다.


저장 프로퍼티에 대한 초기 값 설정

클래스와 구조체는 반드시 클래스나 구조체의 인스턴스가 생성될 때
모든 저장 프로퍼티에 적절한 초기값을 설정해야한다.
저장 프로퍼티는 불확실한 상태로 남아있을 수 없다.

즉, 
var temperature: Double

와 같은 property 가 있으면 반드시 initializer 에서 정의가 필요하다.

저장 프로퍼티에 기본 값을 할당하거나 초기화에서 초기값을 설정할 때
프로퍼티 옵저버는 호출되지 않고 값만 설정된다.



기본 문법

함수 init( ) 키워드를 사용한다.

struct Fahrenheit {

    var temperature: Double

    init() {

        temperature = 32.0

    }

}

var f = Fahrenheit()

print("The default temperature is \(f.temperature)° Fahrenheit")

// Prints "The default temperature is 32.0° Fahrenheit"




기본 값

프로퍼티 선언에서 프로퍼티 기본 값을 지정할 수 있다.

프로퍼티가 항상 같은 초기값을 가져야한다면
초기화에서 설정하는 것보다 기본 값을 제공하는 것이 좋다.

결과는 값지만, 기본값을 주는 것은 
기본 초기화 및 초기화 상속 활용을 쉽게 만든다.

struct Fahrenheit {

    var temperature = 20

}






사용자 정의 초기화

일반적인 함수 정의와 비슷하게 파라미터를 추가한다.


struct Celsius {

    var temperatureInCelsius: Double

    init(fromFahrenheit fahrenheit: Double) {

        temperatureInCelsius = (fahrenheit - 32.0) / 1.8

    }

    init(fromKelvin kelvin: Double) {

        temperatureInCelsius = kelvin - 273.15

    }

}

let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)

// boilingPointOfWater.temperatureInCelsius is 100.0


let freezingPointOfWater = Celsius(fromKelvin: 273.15)

// freezingPointOfWater.temperatureInCelsius is 0.0




매개변수 이름과 인자 레이블

초기화에서 모든 매개 변수에 대해 자동으로 인자레이블 한다.

struct Color {

    let red, green, blue: Double

    init(red: Double, green: Double, blue: Double) {

        self.red   = red

        self.green = green

        self.blue  = blue

    }

    init(white: Double) {

        red   = white

        green = white

        blue  = white

    }

}


let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)

let halfGray = Color(white: 0.5)




인자 레이블 없는 초기화 매개변수

초기화 매개변수에 대해 인자 레이블을 사용하지 않기를 원하면
기본 동작을 재정의하기 위해 매개변수에 대한
인자 레이블을 명시적으로 밑줄 ( _ ) 로 대신 작성한다.

struct Celsius {

    var temperatureInCelsius: Double

    init(_ celsius: Double) {

        temperatureInCelsius = celsius

    }

}


let bodyTemperature = Celsius(37.0)

// bodyTemperature.temperatureInCelsius is 37.0




옵셔널 프로퍼티 타입

사용자 정의 타입이 논리적으로 "값 없음" 을 허용한 저장 프로퍼티를 가진다면
옵셔널 타입 프로퍼티로 선언한다.

옵셔널 타입의 프로퍼티는 자동으로 nil 값으로 초기화된다.

class SurveyQuestion {

    var text: String

    var response: String?

    init(text: String) {

        self.text = text

    }

    func ask() {

        print(text)

    }

}

let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")

cheeseQuestion.ask()

// Prints "Do you like cheese?"


cheeseQuestion.response = "Yes, I do like cheese."



초기화 하는 동안 상수 프로퍼티 할당하기

초기화가 완료될 때 일정한 값을 설정하는 한
초기화 하는 동안 상수 프로퍼티에 값을 할당할 수 있다.

클래스 인스턴스에 대해
클래스를 정의하는 곳에서만 
초기화하는 동안 상수 프로퍼티를 수정할 수 있다.
하위 클래스에서는 수정할 수 없다.

class SurveyQuestion {

    let text: String

    var response: String?

    init(text: String) {

        self.text = text

    }

    func ask() {

        print(text)

    }

}

let beetsQuestion = SurveyQuestion(text: "How about beets?")

beetsQuestion.ask()

// Prints "How about beets?"


beetsQuestion.response = "I also like beets. (But not with cheese.)"







기본 초기화 (Default Initializer)

구조체나 클래스에서 모든 프로퍼티에 대한 기본값을 주면 
기본 초기화를 제공한다.

class ShoppingListItem {

    var name: String?

    var quantity = 1

    var purchased = false

}

var item = ShoppingListItem()



여기서 다른 초기화 메소드를 작성하면
기본 초기화 메소드를 호출할 수는 없다.

class ShoppingListItem {

    var name: String?

    var quantity = 1

    var purchased = false

    

    init(quantity : Int) {

        self.quantity = quantity

    }

}


var item = ShoppingListItem()




구조체 타입에 대한 멤버단위의 초기화

struct Size {

    var width = 0.0, height = 0.0

}


let size = Size()

let twoByTwo = Size(width: 2.0, height: 2.0)




값 타입에 대한 초기화 위임

중복 코드를 제거하기 위해 다른 초기화를 호출할 수 있다. 
스스로 제공하는 다른 초기화에 대해서만 위임이 가능하다.


struct Size {

    var width = 0.0, height = 0.0

}

struct Point {

    var x = 0.0, y = 0.0

}


struct Rect {

    var origin = Point()

    var size = Size()

    init() {}

    init(origin: Point, size: Size) {

        self.origin = origin

        self.size = size

    }

    init(center: Point, size: Size) {

        let originX = center.x - (size.width / 2)

        let originY = center.y - (size.height / 2)

        self.init(origin: Point(x: originX, y: originY), size: size)

    }

}


let centerRect = Rect(center: Point(x: 4.0, y: 4.0),

                      size: Size(width: 3.0, height: 3.0))

// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)






클래스 상속과 초기화

클래스의 모든 저장 프로퍼티는 초기화 하는 동안
반드시 초기 값을 할당해야한다.

Swift 는 클래스 타입에 대해 모든 저장 프로퍼티가 초기 값을 가지도록
도움을 주기 위해, 두 종류의 초기화를 정의한다.
(지정된 초기화, 편리한 초기화)



지정된 초기화와 편리한 초기화

지정된 초기화는 클래스에 대해 우선으로 초기화된다.
지정된 초기화는 클래스의 모든 프로퍼티를 초기화 해주고 

상위 클래스 초기화 과정을 계속하기 위해 
적절한 상위 클래스 초기화를 호출해준다.

모든 클래스는 최소한 하나의 지정된 초기화를 가져야한다.

편리한 초기화는 지정된 초기화의 일부 매개변수를 기본값으로 설정하여
지정된 초기화를 호출하기 위해 정의할 수 있다.

클래스에서 필요하지 않으면 편리한 초기화를 제공할 필요가 없다.


지정된 초기화, 편리한 초기화 문법

init(<#parameters#>) {

    <#statements#>

}

    

convenience init(<#parameters#>) {

    <#statements#>

}




클래스 타입에 대한 초기화 위임

규칙이 3가지가 있다.


1. 지정된 초기화는 반드시 바로 위의 상위 클래스의 지정된 초기화를 호출해야한다.
2. 편리한 초기화는 반드시 같은 클래스의 다른 초기화를 호출한다.
3. 편리한 초기화는 궁극적으로 반드시 지정된 초기화를 호출한다.



두 단계 초기화

첫번째 단계에서 각 저장 프로퍼티는 클래스 시작 시 초기값이 할당된다.
두번째 단계에서는 인스턴스가 사용될 준비가 되기 전 
저장 프로퍼티를 사용자 정의할 수 있는 기회를 준다.

두 단계 초기화는 초기화 되기 전에 프로퍼티 값에 접근하는 것을 막아주고
의도치 않게 다른 초기화에서 다른 값으로 프로퍼티 값이 설정되는 것을 막아준다.



초기화 상속과 오버라이딩

Objective-C 에서의 하위 클래스와 다르게 Swift 에서 하위 클래스는
기본적으로 상위 클래스 상속을 상속받지 않는다.

Swift 는 부모 클래스의 간단한 초기화가 더 특별한 하위 클래스에 상속되는
상황을 방지하고 완전하지 않거나 올바르지 않게 초기화되는
하위 클래스가 새로운 인스턴스를 만드는 사용되는 것을 방지한다.

상위클래스의 지정된 초기화를 재정의할 때 항상 override 를 작성하며
하위 클래스에서 편리한 초기화에 대해 구현할 때도 사용한다.

하위 클래스 초기화를 상위클래스의 편리한 초기화와 일치하게 작성하면
상위 클래스 편리한 초기화는 하위 클래스에 의해 직접 호출될 수 없다.
결과적으로 상위클래스 편리한 초기화와 일치하는 구현을 제공할 때
override 를 작성하지 않는다.

하위클래스는 초기화하는 동안 상속된 변수 프로퍼티는 수정할 수 있지만
상속된 상수 프로퍼티는 수정할 수 없다.

class Vehicle {

    var numberOfWheels = 0

    var description: String {

        return "\(numberOfWheels) wheel(s)"

    }

}


class Bicycle: Vehicle {

    override init() {

        super.init()

        numberOfWheels = 2

    }

}




자동 초기화상속

하위클래스는 기본적으로 상위 클래스의 초기화를 상속하지 않는다.
하지만 특정 조건이 충족되면 자동으로 상속되는데
실제로 이것은 대부분의 경우에 
초기화 재정의 작성이 필요하지 않다는 것을 의미하고
안전한 경우에 최소한으로 상위클래스의 초기화를 상속할 수 있다.



지정된 초기화와 편리한 초기화에서의 동작

아래 예제는 3개의 클래스의 상속을 정의하고
초기화가 동작하는 것을 보여준다.

class Food {

    var name: String

    init(name: String) {

        self.name = name

    }

    convenience init() {

        self.init(name: "[Unnamed]")

    }

}


let namedMeat = Food(name: "Bacon")

let mysteryMeat = Food()


Food 클래스는 String 프로퍼티 name 과
인스턴스 생성에 대해 2개의 초기화를 제공한다.

하나는 지정된 초기화로써, 모든 인자를 initialize 하고 있기 때문에
super.init 호출이 필요하지 않다.
다른 하나는 편리한 초기화로, "[Unnamed]" 값으로
 self.init () 를 호출해주고 있다.


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() //[Unnamed], 1

let oneBacon = RecipeIngredient(name: "Bacon") //Bacon, 1

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



RecipeIngredient 클래스는 새로운 인스턴스 (quantity) 를 모두 채우는데 사용할 수 있는
하나의 지정된 초기화 함수를 가진다.

RecipeIngredient 는 이름만을 받는 편리한 초기화 init (name: String) 을 정의한다.
편리한 초기화는 단순히 지정된 초기화에 quantity 를 1 로 위임한다.

편리한 초기화는 상위클래스의 지정된 초기화를 재정의하기 때문에
반드시 override 키워드를 표시해줘야한다.

RecipeIngredient 클래스는 init(name: String) 초기화를 편리한 초기화처럼 제공할지라도
상위 클래스의 모든 지정된 초기화 구현이 제공된다.
따라서 RecipeIngredient 는 자동적으로 상위 클래스의 모든 편리한 초기화를 상속받는다.


class ShoppingListItem: RecipeIngredient {

    var purchased = false

    var description: String {

        var output = "\(quantity) x \(name)"

        output += purchased ? " " : " "

        return output

    }

}


var breakfastList = [

    ShoppingListItem(),

    ShoppingListItem(name: "Bacon"),

    ShoppingListItem(name: "Eggs", quantity: 6),

]

breakfastList[0].name = "Orange juice"

breakfastList[0].purchased = true

for item in breakfastList {

    print(item.description)

}

// 1 x Orange juice

// 1 x Bacon

// 6 x Eggs


모든 프로퍼티에 대한 기본값을 제공하고
어떠한 초기화도 정의하지 않기 때문에
ShoopingListItem 은 자동적으로 상위클래스로부터
지정된 초기화와 편리한 초기화 모두 상속받는다.






실패할 수 있는 초기화

클래스, 구조체, 열거형의 초기화가 실패할 수 있다는 것에 대해
정의하는 것이 유용할 때가 있다.

실패할 수 있는 초기화는 init 키워드 뒤에 물음표를 붙인다.
실패할 수 있는 초기화는 초기화 타입의 옵셔널 값으로 생성한다.
실패 가능한 초기화에서 return nil 을 추가한다.

엄밀하게는, 초기화는 값을 반환하지 않는다.
초기화가 끝나는 시점에 self 가 완전하게 초기화 된 것을 보증해주기 때문에
return 을 할 필요는 없다.

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"





열거형에 대한 실패할 수 있는 초기화


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





rawValue 를 사용하는 열거형에 대한 실패할 수 있는 초기화


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





초기화 실패전달

클래스, 구조체, 열거형의 실패할 수 있는 초기화는
다른 클래스, 구조체, 열거형에 다른 실패할수 있는 초기화에 위임할 수 있다.

단순하게, 하위 클래스의 실패할 수 있는 초기화는
상위 클래스의 실패할 수 있는 초기화로 위임할 수 있다.

어떤 경우던지, 실패에 대한 초기화를 다른 초기화에 위임하면
모든 초기화 과정이 즉시 실패하고
더 이상 초기화 코드가 실행되지 않는다.


class Product {

    let name: String

    

    init?(name: String) {

        if name.isEmpty {

            return nil

        }

        self.name = name

    }

}


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"



if let zeroShirts = CartItem(name: "shirt", quantity: 0) {

    print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")

} else {

    print("Unable to initialize zero shirts")

}

// Prints "Unable to initialize zero shirts"


if let oneUnnamed = CartItem(name: "", quantity: 1) {

    print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")

} else {

    print("Unable to initialize one unnamed product")

}

// Prints "Unable to initialize one unnamed product"





실패할 수 있는 초기화 재정의 (Overriding a Failable Initializer)

상위클래스의 실패할 수 있는 초기화를 
하위 클래스의 실패하지 않는 초기화로 재정의할 수 있다.
즉, 상위클래스에서 init? 이더라도 하위클래스는 init 이 가능하다.

class Document {

    var name: String?

    

    init() {

    }

    

    init?(name: String) {

        if name.isEmpty {

            return nil

        }

        self.name = name

    }

}


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

        }

    }

}


class UntitledDocument: Document {

    override init() {

        super.init(name: "[Untitled]")!

    }

}


let document = UntitledDocument()

print (document.name!)





필수 초기화 (Required Initializers)

하위 클래스가 반드시 초기화를 구현해야한다는 것을 정의하기 위해
클래스 초기화 정의 앞에 required 작성한다.

class SomeClass {

    required init() {

        // initializer implementation goes here

    }

}


class SomeSubclass: SomeClass {

    required init() {

        // subclass implementation of the required initializer goes here

    }

}


필수 지정 초기화를 재정의할 때에는 override 는 작성하지 않는다.





클로저나 함수로 기본 프로퍼티 값 설정


저장 프로퍼티의 기본값이 약간의 사용자 정의나 설정이 요구되면
프로퍼티에 대한 사용자 정의된 기본 값을 제공하기 위해
클로저나 전역함수를 사용할 수 있다.

타입의 새로운 인스턴스의 프로퍼티가 초기화될 때마다
클로저나 함수는 호출되고
프로퍼티의 기본값으로 할당된 값을 반환한다.

class SomeClass {

    let someProperty: String = {

        // create a default value for someProperty inside this closure

        // someValue must be of the same type as SomeType

        return ""

    }()

}


someProperty 상수에 클로저를 할당하는 것이 아니므로
클로저 마지막에 () 소괄호를 호출해주어
즉시 실행되도록 한다.

위 내용에 대한 예제이다.

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"







'iOS 개발 > Swift' 카테고리의 다른 글

[Swift 3] 프로토콜 (Protocols)  (0) 2017.04.16
[Swift 3] 해제 (Deinitialization)  (0) 2017.04.15
[Swift 3] 상속 (Inheritance)  (0) 2017.04.15
[Swift 3] 서브스크립트 (Subscript)  (0) 2017.04.15
[Swift 3] 메소드 (Method)  (0) 2017.04.15
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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 29 30 31
글 보관함