클로저는 람다 계산식lambda calculus 의 구현체이다.
스위프트에서 클로저란, 내부 함수와 내부 함수에 영향을 미치는 주변 요소를 모두 포함한 객체이다.
클로저는 사실 이름이 있는 함수와 이름이 없는 함수 모두를 포함하는 개념이다.
func 로 선언된 이름이 있는 함수들그러나 통상 "클로저"라고 하면 익명함수를 의미한다.
클로저는 함수이고, 스위프트에서 함수는 1급 객체이다.
따라서 클로저도...
클로저와 함수는 모두 참조 타입이다.
클로저는 클로저 내에서 사용하는 변수/상수의 값을 획득해 두고(캡쳐) 사용한다.
❗️클로저는 값을 캡처할 때, 그 값이 값 타입/참조 타입인 것과 관계없이 캡쳐하는 값들을 참조한다. - "참조 캡처 Reference Capture"
만약 값 타입을 값으로 캡쳐하고 싶다면 캡쳐 리스트 Capture List를 활용해야 한다 - 참고
{ (Parameters) -> ReturnType in
// 실행 구문
}
(Parameters) -> ReturnType실행 구문in : 클로저 헤드와 클로저 바디를 구분짓는 키워드❗️클로저에서는 Argument Label을 사용하지 않는다. 오직 Parameter Name 만 사용한다.
클로저는 호출 구문인 ()를 붙여 실행한다.
호출 방법은 크게 두 가지가 있다
let closure = { () -> String in
return "hello world!"
}
closure()
() 로 감싸고, 마지막에 호출 구문인 ()을 붙인다.({ () -> () in
print("hello world!")
})()
"함수의 마지막 파라미터가 클로저일 때", 클로저의 Argument Label을 생략하고 함수 뒤에 붙여 작성한다.
func doSomething(closure: () -> ()) {
print("do something!")
}
// doSomething 호출 - Inline Closure
doSomething(closure: { () -> () in
print("Hello")
})
// doSomething 호출 - Trailing Closure
doSomething() { () -> () in
print("Hello")
}
"함수의 파라미터가 클로저 하나인 경우", 호출구문 ()도 생략 가능하다.
doSomething { () -> () in
print("Hello")
}함수의 파라미터가 여러 개일 때는 호출구문()을 마음대로 생략해서는 안된다(다른 파라미터를 넘겨 주어야 하므로)
func fetchData(success: () -> (), fail: () -> ()) {
// do something
}
// fetchData 호출 - Inline Closure
fetchData(success: { () -> () in
print("success")
}, fail: { () -> () in
print("fail")
})
// fetchData 호출 - Trailing Closure
fetchData(success: { () -> () in
print("success")
}) { () -> () in
print("fail")
} // 마지막 파라미터의 클로저는 함수 뒤에 붙여 작성
아래 예제를 간소화해 표현해 본다.
func doSomething(closure: (Int, Int) -> Int) {
closure(1, 2)
}
doSomething(closure: { (a: Int, b: Int) -> Int in
return a + b
})
파라미터 타입과 리턴 타입 생략하기
doSomething(closure: { (a, b) in
return a + b
})단축 인자 이름(Shorthand Argument Names) 사용하기
Parameter Name 대신 단축인자를 사용 가능하다.
doSomething(closure: {
return $0 + $1
})
return 생략하기
암시적 반환 표현 : 클로저 내부에 return문 한 줄만 남은 경우, return 표현도 생략 가능
doSomething(closure: {
$0 + $1
})
트레일링 클로저
마지막 파라미터인 클로저를 함수 선언 바깥에 사용
Argument Label 생략 가능
doSomething() {
$0 + $1
}클로저 하나만 파라미터로 전달하는 경우, 호출구문의 소괄호 생략 가능
doSomething {
$0 + $1
}연산자 함수
스위프트의 연산자 역시 일종의 함수이다. 따라서 사용할 클로저의 시그니처가 연산자 함수의 시그니처와 같다면 연산자만 표기해 사용할 수 있다.
let arr1 = [1,2,3]
arr1.reduce(0) { $0 + $1 } // 6
arr1.reduce(0, +) // 6