티스토리 뷰

출처

http://kka7.tistory.com/23



오류 처리란 (Error Handling)

프로그램의 오류를 우아하게 처리하는 과정이다.
이는 어떤 이유때문에 실패했는지 이해하는데 종종 유용하다.

Swift 에서의 오류 처리는 Cocoa 와 Objective C 에서 NSError 를
사용한 오류 처리와 호환된다.


오류를 표시하고 던지기

오류 정의는 Error 프로토콜을 준수하는 타입의 값으로 표현된다.

enum VendingMachineError: Error {

    case invalidSelection

    case insufficientFunds(coinsNeeded: Int)

    case outOfStock

}


throw VendingMachineError.insufficientFunds(coinsNeeded: 5)





오류를 처리하는 방법들


Swift 에서 오류 처리는 호출 스택을 풀지 않으며, 처리 비용이 비싼 과정이다.
이처럼 throw 문의 성능은 return 문과 필적한다.


던지는 함수를 이용하여 오류 전달 (Propagating)

함수, 메소드, 초기화는 오류를 던질 수 있다는 것을 나타내기 위해
throws 키워드를 선언한다.

func canThrowErrors() throws -> String {

    return "canThrow"

}



아래 예에서 vend 함수는 메소드 내부의 모든 오류를 
함수가 호출된 곳으로 전달한다.

struct Item {

    var price: Int

    var count: Int

}


class VendingMachine {

    var inventory = [

        "Candy Bar": Item(price: 12, count: 7),

        "Chips": Item(price: 10, count: 4),

        "Pretzels": Item(price: 7, count: 11)

    ]

    var coinsDeposited = 0

    

    func vend(itemNamed name: String) throws {

        guard let item = inventory[name] else {

            throw VendingMachineError.invalidSelection

        }

        

        guard item.count > 0 else {

            throw VendingMachineError.outOfStock

        }

        

        guard item.price <= coinsDeposited else {

            throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)

        }

        

        coinsDeposited -= item.price

        

        var newItem = item

        newItem.count -= 1

        inventory[name] = newItem

        

        print("Dispensing \(name)")

    }

}


let favoriteSnacks = [

    "Alice": "Chips",

    "Bob": "Licorice",

    "Eve": "Pretzels",

]


func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {

    let snackName = favoriteSnacks[person] ?? "Candy Bar"

    try vendingMachine.vend(itemNamed: snackName)

}


struct PurchasedSnack {

    let name: String

    init(name: String, vendingMachine: VendingMachine) throws {

        try vendingMachine.vend(itemNamed: name)

        self.name = name

    }

}




Do-Try-Catch 를 사용한 오류 처리 

기본적인 구문은 아래와 같다.

do {

    try expression

    statements

} catch pattern 1 {

    statements

} catch pattern 2 where condition {

    statements

}



위 코드에서 처리할 수 있는 오류가 무엇인지 나타내기 위해
catch 뒤에 patterns 을 사용할 수 있다.

catch 절은 do 절의 코드에서 throw 할 수 있는 모든 오류를 처리할 필요는 없다.
하지만 발생한 오류는 어디선가는 반드시 처리되어야한다.

var vendingMachine = VendingMachine()

vendingMachine.coinsDeposited = 8


do {

    try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)

} catch VendingMachineError.invalidSelection {

    print("Invalid Selection.")

} catch VendingMachineError.outOfStock {

    print("Out of Stock.")

} catch VendingMachineError.insufficientFunds(let coinsNeeded) {

    print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")

}

// Prints "Insufficient funds. Please insert an additional 2 coins."





옵셔널 값으로 오류 변경하기

오류 처리를 위해서 옵셔널 값으로 변환하여 try? 를 사용한다.

try? 문법을 수행할 때 오류가 발생하면
그 문법의 값은 nil 이 된다.

func fetchData() -> Data? {

    if let data = try? fetchDataFromDisk() { return data }

    if let data = try? fetchDataFromServer() { return data }

    return nil

}


동일한 방법으로 모든 오류를 처리하길 원할 때
try? 사용으로 간결하게 오류를 처리할 수 있다.



오류 전달을 사용하기 않기

오류 전달을 사용하지 않도록 try! 를 사용할 수 있다.
실행 중에 오류가 발생하지 않을 것임이 명확할 때는
오류 전달을 사용하지 않는 것이 어울린다.

let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")






정리 작업 지정 (Specifying Cleanup Actions, finally)

코드의 현재 블럭을 떠나는 것과 상관없이
수행되어야하는 필요한 정리 작업을 할 수 있도록 (Java 의 finally) 한다.
이 작업을 위해서는 defer 키워드를 사용해야한다.
(ex. file close, memory dealloc ..)

finally 는 꼭 try 아래 있어야하는 것은 아니다.
defer 구문은 현재 영역이 완료될때까지 실행을 미룬다.

func processFile(filename: String) throws {

    if exists(filename) {

        let file = open(filename)

        defer {

            close(file)

        }

        while let line = try file.readline() {

            // Work with the file.

        }

        // close(file) is called here, at the end of the scope.

    }

}



enum DivideError: Error {

    case ZeroDivideError(code: Int)

}


func divide(_ originalValue: Int, _ value: Int) throws -> Int {

    if value == 0 {

        throw DivideError.ZeroDivideError(code: 999)

    }

    return originalValue / value

}


for value in 0..<10 {

    defer {

        print("defer was called")

    }

    

    do {

        try divide(100, value)

    } catch {

        print("error occured")

        break

    }

}








공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
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
글 보관함