클로저는 코드에서 전달 및 사용할 수 있는 독립 기능 블록이며, 1급 객체의 역할을 할 수 있다. 이때 1급 객체란 전달 인자로 보낼 수 있고, 변수/상수 등으로 저장하거나 전달할 수 있으며 함수의 반환 값이 될 수도 있는 객체를 의미한다.
보통 클로저는 이름 없는 함수, 익명 함수를 지칭한다. 하지만, 사실 클로저는 두 개의 형태로 나뉜다.
여기서 named 클로저는 바로 우리가 말하는 함수이다. 그래서 보통 클로저를 지칭할 때는 익명 함수를 뜻한다.
클로저는 다음과 같은 방법으로 나타낸다.
{ (매개 변수) -> 리턴 타입 in
실행 구문
}
익명 함수이기 때문에 func 키워드를 사용하지 않는다. 또한, 클로저는 in
키워드를 중심으로 앞은 closure head
, 뒤는 closure body
라고 한다.
위 표현식을 따라 클로저를 직접 사용해보자.
let hello = { () -> () in
print("hello")
}
hello()
hello
클로저를 호출할 때는, 일반 함수를 호출할 때와 마찬가지이다.
let hello2 = { (name: String) -> String in
return "hello, \(name)"
}
hello2("Hoojeong")
hello, hoojeong
함수에서 배운 것과 마찬가지로 파라미터 네임이 단독으로 쓰였으니 파라미터 이름이자, 전달인자 레이블이라고 생각할 수 있다. 하지만 클로저는 전달인자 레이블을 사용하지 않는다. 따라서 그냥 파라미터 이름이다.
func doSomething(closureParameter: () -> ()) {
closureParameter()
}
doSomething(closureParameter: { () -> () in
print("hello")
})
hello
코드를 보면, 먼저 doSomething 함수는 클로저를 파라미터로 받고, 이를 실행한다. 그 다음, doSomething 함수를 호출할 때 파라미터로 클로저를 작성한다.(중괄호부터 중괄호까지가 클로저!)
이때 closureParameter는 doSomething 함수의 전달인자 레이블이자 파라미터 이다.
func doSomething2() -> () -> () {
return { () -> () in
print("hello")
}
}
doSomething2()()
hello
클로저가 조금 길어지거나 가독성이 떨어진다 싶으면 후행 클로저를 사용하면 좋다. 단, 후행 클로저는 맨 마지막 전달인자로 전달되는 클로저에만 해당되므로 전달인자로 클로저 여러 개를 전달할 때는 맨 마지막 클로저만 후행 클로저로 사용할 수 있다.
아까는 아래처럼 doSomething 함수를 호출할 때, 클로저의 파라미터와 반환 타입을 모두 작성했다.
func doSomething(closureParameter: () -> ()) {
closureParameter()
}
doSomething(closureParameter: { () -> () in
print("hello")
})
이를 후행 클로저를 사용해 바꾸면 아래와 같다.
func doSomething(closureParameter: () -> ()) {
closureParameter()
}
doSomething() {
print("hello")
}
파라미터와 반환 타입이 없는 클로저를 전달 받기 때문에 클로저를 정의할 때 파라미터와 반환 타입을 생략할 수 있고, in 키워드도 생략할 수 있다.
doSomething {
print("hello")
}
다음 함수는 success와 fail이라는 두 개의 파라미터가 선언되어 있다. 이 함수에 다중 후행 클로저를 적용해보자.
func doSomething3(success: () -> (), fail: () -> ()) {
}
doSomething3 {
} fail: {
}
위처럼 파라미터를 중괄호로 각각 구분하고, 첫 번째 파라미터의 이름은 생략한다.
문법을 최적화해 클로저를 단순하게 표현할 수 있다.
아래 doSomething4 함수는 클로저를 파라미터로 하고, 클로저의 파라미터로는 Int 3개를, 클로저의 반환 타입은 Int를 전달한다.
func doSomething4(closure: (Int, Int, Int) -> Int) {
closure(1,2,3)
}
doSomething4(closure: { (a: Int, b: Int, c: Int) -> Int in
return a+b+c
})
그렇다면, 이제 경량 문법을 적용한 코드들을 살펴보자.
doSomething4(closure: { (a, b, c) in
return a+b+c
})
각 파라미터를 $
와 index
로 대체한다.
따라서 a -> $0, b -> $1, c -> $2
가 된다.
doSomething4(closure: {
return $0 + $1 + $2
})
클로저 코드 블럭 내에 return 구문만 있을 경우, return 키워드를 생략할 수 있다.
doSomething4(closure: {
$0 + $1 + $2
})
doSomething4(closure: {
print("hello")
$0 + $1 + $2
})
doSomething4() {
print("hello")
$0 + $1 + $2
}
doSomething4 {
print("hello")
$0 + $1 + $2
}