안녕하세요. iOS앱을 개발하면서 Swift로 에러처리에 대한 글을 작성해보려고 합니다.
에러처리를 적절하게 선택하면 코드 품질이 향상되고 프로그래머 의도가 명확해집니다.
그래서 언제 무엇을 사용해야하는지를 간단하게 적어보겠습니다.
에러는 크게 두 가지 범주로 나눌 수 있습니다.
프로그래머가 제어할 수 없는 런타임에 대한 에러(Optional, throw) - recoverable
프로그래머의 실수에 대한 에러 (Assertion, FatalError) - non-recoverable
1)Optional
옵셔널은 값이 있을 경우, 없으 경우에 대한 결과를 나타냅니다.
try? 표현식을 사용합니다.
언제?
- 에러가 간단할 때
예제
func someThrowingFunction() throws -> Int {
// ...
}
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
2)Throwing an Error
Throw(던지다) 즉, 에러를 던지는 것을 의미합니다. 잠재적인 에러 처리를 위해 호출부에서 do, try, catch문을 이용하여 작성합니다.
언제?
- 메소드 내에서 오류를 로컬로 처리할 수 없을 때
- 오류가 동기적으로 전파되어야될 때
- 로직 플로우가 변경되어야 될 때
- 한 곳에서 여러 오류처리가 필요할 때
예제 - 던지는 함수를 사용하여 오류 전달 및 do, try, catch 사용하기
enum VendingMachineError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}
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)")
}
}
var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
try vendingMachine.vend(itemNamed: "Pretzels")
} catch VendingMachineError.invalidSelection {
//
} catch VendingMachineError.insufficientFunds(_) {
//
} catch VendingMachineError.outOfStock {
//
}
3)Assertion
코드가 실행될 때 반드시 만족해야하는 조건을 코드 상에 명시해 놓는 것입니다.
- assert(), assertionFailure()
디버깅 모드에서만 작동하고 디폴트는 크래시를 발생시킵니다. (런타임 강력 경고)
(assert를 많이 사용해도 실제 프로덕션 앱의 성능에 영향을 끼치지 않습니다!)
언제?
- 프로그래머 실수를 추적하는데 적합.
예제
var someInt: Int = 0
assert(someInt == 0, "someInt != 0")
// 조건이 충족되지 않는다면 실행되지 않음.
print("someInt == 0")
- precondition(), preconditionFailure()
assert()와 차이점은 디버그/릴리즈 빌드 모두 검증된다는 점입니다. 조건을 만족하지 못하면 다음 플로우가 진행되지 않습니다.
언제?
- 외부 요인에 대한 에러
예제
var someInt: Int = 0
precondition(someInt == 0, "someInt != 0")
// 조건이 충족되지 않는다면 실행되지 않음.
print("someInt == 0")
4)Fatal Error
호출 즉시 크래시를 발생시킵니다. (프로세스를 죽임)
언제?
- 에러가 치명적일 때
- 메소드에서 리턴할 것이 없을 때
예제
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? TableViewCell else {
fatalError("The dequeued cell is not an instance of TableViewCell.")
}
return cell
}
reference
https://jusung.gitbook.io/the-swift-language-guide/language-guide/17-error-handling
'Swift' 카테고리의 다른 글
[Swift] rethrows란 (0) | 2020.02.23 |
---|---|
[Swift] Extension이란 (0) | 2020.01.05 |
[Swift] SwiftLint 적용하기 (0) | 2020.01.05 |
[Swift] Extension(확장)으로 코드 가독성 올리기 (0) | 2020.01.05 |
[Swift] 구조체란 무엇인가? (0) | 2020.01.03 |