자동 클로저는 말 그대로, 특정 표현식을 함수의 매개변수로 전달할 때 그 표현식을 클로저로 자동으로 감싸주는 기능이에요. 쉽게 말해, 복잡하게 중괄호({ })로 감싸주지 않아도 스위프트가 알아서 클로저로 만들어 준다는 거죠.
자동 클로저는 인수를 따로 받지 않고, 호출될 때 내부에 있는 표현식의 결과를 반환해 줍니다.
자동 클로저의 진짜 매력은 지연 평가예요. 코드가 바로 실행되지 않고, 클로저가 호출될 때만 실행된다는 거죠. 이렇게 하면 코드 실행 시기를 우리가 제어할 수 있어서, 사이드 이펙트가 있거나 계산이 오래 걸리는 경우에 딱 좋아요.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints "5"
print("Now serving \\(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
위 코드에서 볼 수 있듯이, customerProvider
는 클로저로 배열의 첫 번째 요소를 삭제하는 코드를 담고 있지만, 이 클로저가 호출되기 전까지는 배열이 그대로 유지됩니다. 호출 전까지는 아무 일도 일어나지 않아요. 덕분에 클로저를 호출하지 않으면, 배열의 요소는 삭제되지 않고 그대로 남아있죠.
그래서 customerProvider
의 타입은 String
이 아니라 () -> String
타입이에요. 호출하기 전까지는 그저 함수처럼 보관만 되는 거죠.
자동 클로저를 사용할 때는 클로저를 명시적으로 작성할 필요가 없어요. 그냥 표현식만 전달하면, 스위프트가 알아서 클로저로 변환해 줍니다.
이렇게 하면 지연 평가 덕분에 코드가 실행되는 시점을 우리가 컨트롤할 수 있죠.
// 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!"
여기서 @autoclosure
를 추가해 주면, 중괄호도 생략할 수 있어요.
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \\(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// Prints "Now serving Ewa!"
자동 클로저와 탈출 클로저를 함께 사용하려면, @autoclosure
와 @escaping
을 둘 다 붙여주면 돼요.
// customersInLine is ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))
print("Collected \\(customerProviders.count) closures.")
// Prints "Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving \\(customerProvider())!")
}
// Prints "Now serving Barry!"
// Prints "Now serving Daniella!"
여기서는 @escaping
덕분에, 클로저가 함수의 실행이 끝난 후에도 살아남아, 나중에 호출할 수 있게 되는 거죠. 덕분에 클로저를 저장해두고 나중에 호출할 수 있어요.
@escaping
과 함께 사용하면, 자동 클로저를 나중에 호출할 수 있도록 저장 가능.출처
https://bbiguduk.gitbook.io/swift/language-guide-1/closures#autoclosures