closure 정리
Closure
-
Closure는 익명함수로 알려진 기능
-
func키워드로 선언하는 것이 아니라, 함수를변수에 선언하는 형태를 취하고 있다 -
Closure는 코드를 간결하고, 직관적으로 작성하는데 많은 도움을 주는 기능
일반적 함수의 사용과 Closure의 사용 방식의 차이
일반적 함수 사용
var counter = 0
func addCounter() {
counter += 1
}
addCounter()
addCounter()
print(counter) // 결과값 2
- 일반적인 함수는 위와 같이 함수명(addCounter)을 설정하고, 해당 함수명으로 함수를 호출하는 형태를 취한다.
Closure
var counter = 0
let addCounter = {
counter += 1
}
addCounter()
addCounter()
print(counter) // 결과값 2
-
Closure는 변수에 값을 선언하는 대신 변수에
함수를 선언한다. -
위와 같이
addCounter변수에함수를 선언하였다. -
그리고 이 변수(addCounter)는 함수처럼 호출을 할 수 있다 ->
addCounter()의 형태
Closure 기본 형태
- Closure는 기본적으로
Header와Body를 가진 형태로 구성되어 있다.
var closure = { Header in Body }
-
여기서
Header에서는인자와 리턴타입을 명시한다. -
Body에서는 호출시실행되는 함수의 내용을 작성. -
in키워드는Header와Body를 나누는 키워드이다.

sorted(by: )를 통한 Closure 설명
-
sorted(by: )메소드는 Swift 내장 메소드로 배열을by:이하의 기준에 따라 정렬하는 메소드이다. -
그 기준은 배열에 있는 데이터 타입들 간의
대소 비교를 통해 이뤄진다.- 즉, 배열의 두 값을 가져와 그 크기를 비교하여 앞의 값이 뒤의 값보다 이전에 와야하면
true, 그렇지 않으면false를 반환하는 것을 반복하고 그 결과에 따라 정렬한다
- 즉, 배열의 두 값을 가져와 그 크기를 비교하여 앞의 값이 뒤의 값보다 이전에 와야하면
let names = ["Chris", "Jiyeon", "Jun", "Sua", "Daniella"]
let numbers = [4,5,6,3,1]
// 배열 내의 데이터 타입에 따라 필요한 인자가 다르다.
names.sorted(by: (String, String) -> Bool)
numbers.sorted(by: (Int, Int) -> Bool)
-
by에서 필요한 값은Bool을 반환하는함수의 형태->일반적인 함수혹은Closure가 들어가게 된다 -
아래와 같은 형태들이
sorted(by: )의 인자로 들어가게 된다
let names = ["Chris", "Jiyeon", "Jun", "Sua", "Daniella"]
let numbers = [2,6,4,7,1]
// a1이 a2보다 클 때, 앞에 와야한다를 의미
// "Sua"가 "Jun" 보다 크고 이 때 true이므로 "Sua"가 "Jun"보다 배열의 앞쪽에 위치한다.
// 결과적으로 배열 전체가 내림차순으로 정렬된다
// 일반적인 함수의 형태
func reverse(_ a1: String, _ a2: String) -> Bool {
return a1 > a2
}
// 일반적인 함수를 들어가게 함
var nameReverse = names.sorted(by: reverse) // 결과값 ["Sua", "Jun", "Jiyeon", "Daniella", "Chris"]
var numberReverse = numbers.sorted { (_ a1: Int, _ a2: Int) -> Bool in
return a1 > a2
} // 결과값 [7, 6, 4, 2, 1]
Closure 축약
-
Closure의
짧지만 직관적인 코드작성에 크게 기여하는 것이축약형 -
어떤 경우축약을 하는지모른다면어떻게Closure가 작동하는지 모르는 상황에 직면
1. Type Inferring
- Closure는
어떤 타입의 데이터가 인자로 들어오고,return 값이 어떤 것인지 미리 알고 있다면이를생략할 수 있다
names.sorted { (a1: String, a2: String) -> Bool in
return a1 > a2
}
// 데이터 타입 생략
names.sorted { (a1, a2) -> Bool in
return a1 > a2
}
-
sorted(by:)메소드의파라미터 함수는 항상배열의 데이터 타입을 가진 인자 두개를 지닌다그리고Bool 타입을리턴한다. -
즉, 어떤 데이터 타입이 필요한지 이미 알려져 있어서 Closure는 이를 추론 할 수 있다.- 그러므로 모두
생략가능하다.
- 그러므로 모두
var plus: (Int, Int) -> Int = { (a: Int, b: Int) in return a + b}
// 데이터 타입 생략
var plus: (Int, Int) -> Int = { (a, b) in return a + b }
-
위의
plus는 받은 두 값을 더한 값을 반환하는 변수-
이 때, 여기서는 변수를 선언할 때 타입을
(Int, Int) -> Int로명시를 했기 때문에,Closure에서이미 어떤 데이터 타입이 인자로 오고 어떤 데이터 타입을 리턴하는지 알고 있다 -
그러므로 Closure 내부에서 이를 생략할 수 있다
-
2. Single Expression Closure 의 “return” 키워드 생략
-
Single Expression Closure는
return키워드를생략할 수 있다. -
아래에서 Single Expression =
a1 > a2,n1 + n2
names.sorted { (a1, a2) -> Bool in a1 > a2 }
var plus: (Int, Int) -> Int = {(n1, n2) in n1 + n2 }
3. Short-hand argument name
Closure 내부로들어오는 인자들은항상 이름을 정의하지 않아도, 순서대로$0, $1의 이름으로 사용할 수 있다.
names.sorted(by: {$0 < $1} )
var plus: (Int, Int) -> Int = { $0 + $1 }
4. Operator Methods를 통한 축약
Operator(연산자)를 이용한 축약은두 값을연산하는 것이결과로 나오는특별한 경우이기 때문에 사용할 수 있는 축약이다.
names.sorted(by: <)
var plus: (Int, Int) -> Int = (+)
-
sorted(by:)메소드는항상 두 값의 크기 비교를 통해Bool을 반환하므로연산자(<)만쓰는 것으로도그의미를알 수 있다. -
plus의 경우에도항상 두 값을 더한 값을반환하므로연산자(+)만으로도연산을 알 수 있다. -
때문에 위와 같은 축약이 가능하다.
함수로 Closure 전달하기
-
Closure는변수에저장되기 때문에변수를함수에넘길 수 있는것처럼,Closure도함수로 넘길 수 있다-
그 형태는일반적인변수를 넘기는 것과 동일 -
func 함수명(label 변수명: 변수타입)이 조건에 맞게만 써주면 된다.
-
var hi: () -> Void = { print("Hi!") }
var bye: () -> Void = { print("bye bye~") }
func runClosure(name aClosure: () -> Void) {
aClosure()
}
runClosure(name: hi) // Hi!
runClosure(name: bye) // bye bye~
Trailing Closure를 활용한 Syntax
Trailing Closure는함수의 호출시 Closure가지나치게길어질 경우이를함수와 분리해서 쓸 수 있는 Syntax Sugur 이다.
runClosure() {
// aClosure() 가 호출된 시점에서 실행
print("trailing 1")
}
// 인자가 Closure 밖에 없다면 ()를 생략할 수 있다
runClosure {
print("trailing 2")
}
- 인자가 한 개가 아닌 경우 다음과 같이 사용할 수 있다.
// 인자가 한 개가 아닌 경우
func runClosure2(index: Int, name aClosure: () -> Void) {
aClosure()
}
runClosure2(index: 2) {
// index는 2로 넘기고, aClosure()가 호출된 시점에서 Hello! 출력
print("Hello!")
}
Map Method
-
Trailing closure 형태로 사용하는 대표적인 메소드 중 하나
map(_:) -
map(_:)은collection 데이터 타입 안의 모든 객체(혹은 일부)를 수정할 때사용하는 메소드- 일종의 루프, 일반적인 루프라기 보다는 값을 새롭게
mapping하는데 목적이 강하다
- 일종의 루프, 일반적인 루프라기 보다는 값을 새롭게
var variableNumber = [1,5,6,2,8]
variableNumber = variableNumber.map({ (value) -> Int in
var newVal = value + 1
return newVal
})
-
위의 예시는
variableNumber배열의 값들을 기존 값 + 1의 값들로 새롭게mapping -
위의 예시처럼 기존과 동일한 리턴 타입을 가질 수도 있지만 그렇지 않아도 된다
let digitName = [0:"Zero", 1:"One", 2:"Two", 3:"Three", 4:"Four", 5:"Five", 6:"Six", 7:"Seven", 8:"Eight", 9:"Nine"]
let oddOrEvenArr = digitName.map { (key, value) -> String in
var str = ""
if (key % 2) == 0 {
str = "짝수"
} else {
str = "홀수"
}
return str
}
print("🔵: \(oddOrEvenArr)")
// 🔵: ["홀수", "홀수", "홀수", "짝수", "짝수", "짝수", "짝수", "홀수", "짝수", "홀수"]
let oddOrEvenDic = digitName.map { (key, value) -> [Int: String] in
var str = ""
if (key % 2) == 0 {
str = "짝수"
} else {
str = "홀수"
}
return [key: str]
}
print("🔵: \(oddOrEvenDic)")
🔵: [[1: "홀수"], [3: "홀수"], [8: "짝수"], [0: "짝수"], [4: "짝수"], [5: "홀수"], [9: "홀수"], [2: "짝수"], [6: "짝수"], [7: "홀수"]]
- 위의 예시는 각각 digitName을 새롭게 mapping 하여 만든 배열과 딕셔너리이다.