아카데미에서 프로젝트를 할때 가장 못했던 부분을 이야기하라면 1초도 고민을 안하고 error handling이라고 이야기할 수 있을만큼 에러처리를 하나도 안해서 문제가 생겼던적이 많다.
그런의미에서 에러처리는 잘해야하고 잘알아야하는 개념이라는걸 ⭐️너무나 많은⭐️삽질을 통해 알게되었다. 사실 어쩌면 우리가 optional을 벗기는 것도 에러처리중에 하나라는 생각이들기는했다. 예전에 정말 아무것도 모르던 시절에는 강제언레핑만 주구장창 썼다... 하지만이제 그러지는 않는걸보니 그때에 비해 성장하긴했다는 생각이 든다.
에러처리를 하는방법은 한가지가 아니다 그렇기때문에 내가 편하거나 아니면 팀에서 정한 방법으로 사용하면된다. 결국 어떤 방식이든 에러처리는 네트워크와의 통신 혹은 앱을 구동하는데있어서 "어떤 문제가 발생할 가능성이 있는 곳에 미리 문제가 생기면 여기를 한번 보러와주세요~"의 느낌이 커서 미리 코드를짤때부터 여기서 이런문제가 발생할수있겠구나를 안다면 앱에 문제가생겼을때 빨리 해결할수있다.
근데 앱출시를 몇개 해보니 느낀건데 약간의 기본 틀은 있는거같다. 네트워크 할때 발생하는 에러는 특정하게 몇개로 정해져있기는한거같다. 그래서 기본적인 error case와 handling만 할줄알면 기본기는 다 잡은거라고 할수있을거같다.
//에러 프로토콜 채택 (약속)
enum HeightError: Error {
case maxHeight
case minHeight
}
// 에러를 던질수 있는 함수 타입이다
func checkingHeight(height: Int) throws -> Bool {
if height > 190 {
throw HeightError.maxHeight
} else if height < 130 {
throw HeightError.minHeight
} else {
// 정상적인 경우는 함수의 return형인 true false를 리턴
if height >= 160 {
return true
} else {
return false
}
}
}
// 정상적인 경우의 처리 상황
do {
// 함수를 실행하면 무조건 try를 붙여야하고(에러발생가능성있는함수라면)
// 무조건 do문안에 있어야한다
let isChecked = try checkingHeight(height: 200)
print("놀이기구 타는 것 가능: \(isChecked)")
// 비정상적인 경우의 처리 상황
} catch {
print("놀이기구 타는 것 불가능")
}
do {
let isChecked = try checkingHeight(height: 200)
if isChecked {
print("청룡열차 가능")
} else {
print("후룸라이드 가능")
}
} catch {
print("놀이기구 타는 것 불가능")
}
let isChecked = try? checkingHeight(height: 170)
let isChecked2: Bool = try! checkingHeight(height: 150)
do {
let isChecked = try checkingHeight(height: 100)
print("놀이기구 타는 것 가능: \(isChecked)")
} catch HeightError.maxHeight {
print("키가 커서 놀이기구 타는 것 불가능")
} catch HeightError.minHeight {
print("키가 작아서 놀이기구 타는 것 불가능")
}
do {
let isChecked = try checkingHeight(height: 100)
print("놀이기구 타는 것 가능: \(isChecked)")
} catch {
// catch문에는 error라는 상수가 제공됨
// 실제 우리가 정의한 구체적인 에러 타입이 아니라 구체화 해야함
// 범용적인error에서 구체적인 error로 다운캐스팅해줘야함
if let error = error as? HeightError {
switch error {
case .maxHeight:
print("키가 커서 놀이기구 타는 것 불가능")
case .minHeight:
print("키가 작아서 놀이기구 타는 것 불가능")
}
}
}
func someFunction1(callback: () throws -> Void) rethrows {
// catch문 없어도 괜찮음 그런경우엔 do없어도 괜찮음
try callback()
}
1.에러 enum(Error프로토콜을채택한)정의
enum NameError: Error {
case noName
}
class Course {
var name: String
2.에러발생할수있는 경우에 throws 키워드 사용
init(name: String) throws {
3.분기처리
if name == "" {
throw NameError.noName
} else {
self.name = name
print("수업을 올바르게 생성")
}
}
}
do {
4.에러발생이 가능한 함수 실행문 앞에 try -> do문에서 실행
let _ = try Course(name: "스위프트5")
5.에러가 발생한다면 실행시킬 실행문을 catch문에 정의
} catch NameError.noName {
print("이름이 없어 수업이 생성 실패하였습니다.")
}
(상위) throws (하위) throws재정의 (O 가능)
(상위) 일반 (하위) throws재정의 (O 가능)
(상위) throws (하위) 일반재정의 (X 불가능)
(상위) throws (하위) rethrows재정의 (O 가능)
(상위) rethrows (하위) throws재정의 (X 불가능)
func deferStatement1() {
defer {
print("나중에 실행하기")
}
print("먼저 실행하기")
}
deferStatement1() //먼저 실행하기 -> 나중에 실행하기
func deferStatement2() {
if true {
print("먼저 실행하기")
return
}
// 디퍼문이 호출되어야, 해당 디퍼문의 실행이 예약되는 개념
defer {
print("나중에 실행하기")
}
deferStatement2() //먼저 실행하기(나중에실행하기는 실행자체가 안됨)
// 등록한 역순으로 실행
func deferStatement3() {
// 가장마지막에 실행
defer { print(1) }
defer { print(2) }
// 가장 먼저 실행
defer { print(3) }
}
deferStatement3() // 321
for i in 1...3 {
defer { print ("Defer된 숫자?: \(i)") }
print ("for문의 숫자: \(i)")
}
한사이클마다 defer가 실행되기때문에 123순서가 바뀌지 않는다
for문의 숫자: 1
Defer된 숫자?: 1
for문의 숫자: 2
Defer된 숫자?: 2
for문의 숫자: 3
Defer된 숫자?: 3