defer 구문은, defer 구문이 있는 곳에서, 프로그램 컨트롤을 스코프 바깥으로 옮기기 바로 직전에 실행하려는 코드에 사용한다.
defer {
// statements
}
defer 문 안에 있는 내용은 프로그램 제어가 어떻게 이동되는지와는 관계 없이 실행된다. 예를 들어 수동적으로 자원관리가 들어갈 때, 에러가 발생되더라도 액션을 수행해야 할 때 사용한다.
Defer가 실행되는 순서
여러개의 defer문이 있을 때
같은 스코프 내에 여러개의 defer 문이 있으면, 실행되는 순서는 보이는 순서의 역순. 마지막 defer 문을 첫번째로 실행한다는 의미는, 마지막 defer 문 안의 내용은 다른 defer 문에 의해서 정리되는 리소스를 참조할 수 있다는 뜻.
func 디퍼함수() {
defer { print("첫번째") }
defer { print("두번째") }
print("마지막")
}
디퍼함수()
// 마지막
// 두번째
// 첫번째
defer문이 중첩되어 있을 때
중첩되어 있을 경우도 가장 바깥쪽에 있는 defer부터 실행된다.
func 중첩디퍼함수() {
defer {
defer {
defer {
print("중첩 첫번째")
}
print("중첩 두번째")
}
print("중첩 마지막")
}
}
중첩디퍼함수()
// 중첩 마지막
// 중첩 두번째
// 중첩 첫번째
일반적인 용례
컨텍스트를 열거나 닫을 때
가장 일반적으로 사용하는 케이스는 스코프 안에서 컨텍스트를 열거나 닫을 때. 예를 들어서 파일 접근에 대해 다룰 경우. FileHandle 은 한 번 접근이 끝나면 반드시 닫아줘야하는데 이때 defer 구문을 이용해 잊지 않고 닫아줄 수 있다.
func writeFile() {
let file: FileHandle? = FileHandle(forReadingAtPath: filepath)
defer { file?.closeFile() }
// 파일관련된 코드들 등등
}
결과값
컴플리션 콜백에서 반환되는 결과값 관련하여 사용.
func getData(completion: (_ result: Result<String>) -> Void) {
var result: Result<String>?
defer {
guard let result = result else {
fatalError("We should always end with a result")
}
completion(result)
}
// 결과값 생성하기
}
이렇게 해서 결과값을 항상 검증하고 컴플리션 핸들러를 실행하게 함. 결과값이 nil 일 경우, fatalError 가 던져지고 앱이 fail됨
로딩 인디케이터
func performLogin() {
shouldShowProgressView = true
defer {
shouldShowProgressView = false
}
do {
let _ = try await LoginManager.performLogin()
DispatchQueue.main.async {
self.coordinator?.successfulLogin()
}
} catch {
let error = error
showErrorMessage = true
defer가 실행되지 않는 상황
defer문이 읽히기 전에 종료가 되는 상황일 때
import UIKit
var greeting = "Hello, playground"
func deferSometimeRun(value: Int?) {
guard let value = value else {
print("nil이라서 종료")
return
}
defer {
print("#DEFER: 값이 \\(value) 일 때 실행됐어요.")
}
print("값 : \\(value)")
}
deferSometimeRun(value: nil)
deferSometimeRun(value: 6)
// nil이라서 종료
// 값 : 6
// #DEFER: 값이 6 일 때 실행됐어요.
func deferAlwaysRun(value: Int?) {
defer {
print("#DEFER: 값이 \\(value) 일 때 실행됐어요.")
}
guard let value = value else {
print("nil이라서 종료")
return
}
print("값 : \\(value)")
}
deferAlwaysRun(value: nil)
deferAlwaysRun(value: 7)
// nil이라서 종료
// #DEFER: 값이 nil 일 때 실행됐어요.
// 값 : 7
// #DEFER: 값이 Optional(7) 일 때 실행됐어요.
때문에 defer 구문은 항상 해당 스코프 가장 윗쪽에 해두는 것을 권장. (왜냐면 defer문 쓰는 것 자체가 종료를 지연하고 마지막으로 실행하는 것이기 때문에 )
참고링크
Statements - The Swift Programming Language (Swift 5.7)
A complete guide to the Swift defer statement - LogRocket Blog
오늘도 읽어주셔서 감사합니다.
궁금하거나 나누고 싶은 얘기가 있으시면 댓글로 알려주세요!
재밌게 읽으셨다면 공감과 구독은 큰 힘이 됩니다.
항상 감사합니다.
이 글은 doy.oopy.io 에도 발행되어 있습니다.
'💻 Programming 개발 > 🍎 iOS 개발, Swift' 카테고리의 다른 글
RelativeTimeFormatter로 상대시간 표기하기 (0) | 2023.03.07 |
---|---|
Moya 간단 사용법 - Request sample부터 실제 뷰컨트롤러에서 사용까지 (0) | 2023.02.23 |
[WWDC 2022] Embrace Swift Generics (0) | 2023.02.14 |
[Objective-C] .h와 .m 파일의 연결성 (0) | 2023.01.30 |
[Swift][번역] 스위프트의 자료구조와 알고리즘 - 섹션 2. 기초 자료구조 - 챕터7. 연결리스트 도전과제 (0) | 2022.07.11 |
댓글