글은 swift에서 function에서 escaping과 non-escpaing이 무엇이 다른지 기록하기 위해 작성되었습니다.
1. Escaping Colsure란?
클로저는 함수의 인자로 전달되었지만 함수가 반환된 이후에 호출될 때, 이를 함수에서 벗어나는(escaping) 클로저라고 합니다. 만약 클로저가 함수의 매개변수 중 하나로 전달될 경우, 해당 클로저가 탈출 가능하다는 것을 나타내기 위해 매개변수의 타입 앞에 @escaping 키워드를 작성할 수 있습니다.
클로저가 escape할 수 있는 한 가지 방법은, 클로저가 함수 외부에 정의된 변수에 저장되는 경우입니다. 예를 들어, 비동기 작업을 시작하는 많은 함수들은 완료 핸들러(completion handler)로 클로저를 인자로 받습니다. 이러한 함수는 작업을 시작한 후 즉시 반환하지만, 작업이 완료되기 전까지 클로저는 호출되지 않습니다. 따라서 클로저는 나중에 호출될 수 있도록 탈출(escape)해야 합니다.
2. CompletionHandler를 받을때 왜 @escaping 키워드를 안쓰면 어떻게 될까?
func test1(completion: () -> Void) -> Int{
let myCompletion: () -> Void = {
print("complete")
}
let request = URLRequest(url: .init(string: "https://example.com")!)
URLSession.shared.dataTask(with: request) { data, response, error in
completion() // ❌ 컴파일 에러 발생
}
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
completion() // ❌ 컴파일 에러 발생
}
return 1
}
func test2(completion: @escaping () -> Void) -> Int{
let myCompletion: () -> Void = {
print("complete")
}
let request = URLRequest(url: .init(string: "https://example.com")!)
URLSession.shared.dataTask(with: request) { data, response, error in
completion() // ✅
}
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
completion() // ✅
}
return 1
}
+ Autoclosure란?
autoclosure는 함수의 인자로 전달되는 표현식을 자동으로 감싸는 클로저입니다. 인자를 받지 않으며, 호출될 때 내부에 감싸진 표현식의 값을 반환합니다. 이러한 문법적 편의성 덕분에 함수의 매개변수 주위에 중괄호를 생략하고 명시적인 클로저 대신 일반 표현식을 작성할 수 있습니다.
아래와 같이 오토 클로저를 생성하고 Delay후에 autoclosure를 실행할 수 있습니다.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"
let customerProvider = { customersInLine.remove(at: 0) } // ✅ Use autoclosure
print(customersInLine.count)
// Prints "5"
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
위의 autoclosure와 마찬가지로 함수로 다음과 같이 구현할 수 있습니다.
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!"
위 내용에서 나온 serve(customer:) 함수는 고객의 이름을 반환하는 명시적인 클로저를 매개변수로 받습니다. 아래 버전의 serve(customer:) 함수는 동일한 동작을 수행하지만, 명시적인 클로저 대신 매개변수 타입에 @autoclosure 속성을 표시하여 자동 클로저를 받습니다. 이제 이 함수를 클로저 대신 String 인자를 받는 것처럼 호출할 수 있습니다. customerProvider 매개변수의 타입이 @autoclosure 속성으로 표시되어 있기 때문에 인자가 자동으로 클로저로 변환됩니다.
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
실제 오토클로저를 활용한 테스트들 입니다.
func test3(completion: () -> String) -> String {
return completion()
}
func test4(completion: @autoclosure () -> String) -> String {
return completion()
}
func test5() {
var myArray = ["Minsu", "Susan", "Chan", "Heize"]
print(test3{ myArray.removeFirst() })
print(test3(completion: myArray.removeFirst() )) // ❌ Compile Error
let myClosure: () -> String = {
myArray.removeFirst()
}
print(test4(completion: myClosure()))
print(test4{ myArray.removeFirst() }) // ❌ Compile Error
print(test4(completion: myArray.removeFirst()))
}
'Swift' 카테고리의 다른 글
[Swift] 왜 @objc 키워드를 사용해서 UIButton에 매핑해야 될까요? (3) | 2024.12.29 |
---|---|
Approach to load testing iOS app (stackoverflow 질문글 한국어 해석) (2) | 2024.12.03 |
[iOS] 모듈화 하기 vs 그냥 살기. (주관적으로 느낀 모듈화 장점 3가지) (0) | 2024.11.22 |
[iOS] SwiftData를 활용한 간단한 Todo 어플리케이션 만들어 보기 (0) | 2024.06.02 |
[iOS] Weak Dictionary 다이브 (1) | 2024.02.16 |