컴퓨터 공학 용어에서의 클로저(closure)는 함수나 클로저 표현식과 같은 독립적인 코드 블록과 코드 블록 주변에 있는 하나 이상의 변수가 결합된 것을 말한다.
아래 코드에서 functionA는 functionB라는 이름의 함수를 반환한다.
사실 functionB는 functionB 내부 영역 밖에 선언된 counter 변수에 의존하기 때문에 functionA는 클로저를 반환하고 있다.
다시 말해, functionB는 counter 변수를 "잡고 있다(captured)" 또는 "가두고 있다(closed over)"라고 말할 수 있으므로 전통적인 컴퓨터 공학 용어인 클로저(closure)로 간주된다.
Swift에서는 클로저(closure)와 클로저 표현식(closure expression) 용어가 혼용되기 시작했지만, 어쨌든 둘 다 지원한다.
// 간단한 클로저 예제
func functionA() -> () -> Int {
var counter = 5
// functionA 함수 안에 functionB 함수 선언 - 중첩된 함수
func functionB() -> Int {
// functionB 밖의 counter를 사용한 계산값을 반환한다
return counter + 30
}
let result = functionB()
print("\(result)")
// functionB() 이것은 functionB를 호출하는 문구
// functionB 이것은 functionB 함수 그 자체를 지칭함
return functionB
}
// functionA() 호출의 결과는 functionB 함수이고
// functionB 함수 안에는 counter값이 계속 붙잡혀 활용이 된다
// myClosure가 곧 functionB이기 때문에 myClosure가 존재하는 동안 counter도 붙잡혀 존재한다
let myClosure = functionA()
let result = myClosure()
print("\(result)")
// 배열 정렬 예제
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
var defaultNames = names.sorted()
print(defaultNames)
// 정렬 기준을 만들어주는 함수 만들기
func backward(_ s1: String, _ s2: String) -> Bool {
// print("\(s1)과 \(s2)를 비교합니다")
return s1 > s2
}
var reverseNames = names.sorted(by: backward)
print(reverseNames)
// 전역함수 backward와 똑같이 작동하는 클로저 표현식을 만들어
// sorted의 by: 매개변수로 인라인 클로저라는 구현법으로 바로 써서 보낸다
reverseNames = names.sorted(by: { (_ s1: String, _ s2: String) -> Bool in
return s1 > s2
})
print(reverseNames)
// 한줄로도 가능
reverseNames = names.sorted(by: { (_ s1: String, _ s2: String) -> Bool in return s1 > s2 })
print(reverseNames)
// 더 짧게
// 배열이 String 문자열들로 채워진 걸 아니까
reverseNames = names.sorted(by: { (_ s1, _ s2) -> Bool in return s1 > s2 })
print(reverseNames)
// 더 짧게
reverseNames = names.sorted(by: { s1, s2 in return s1 > s2 })
print(reverseNames)
// 더 짧게
reverseNames = names.sorted(by: { s1, s2 in s1 > s2 })
print(reverseNames)
// 짧은 인자이름으로 대체하는 방법도 있음
reverseNames = names.sorted(by: { $0 > $1 })
print(reverseNames)
// 연산자 메서드
reverseNames = names.sorted(by: >)
print(reverseNames)
// Array의 map 매소드 예제
let digitNames: [Int: String] = [
0: "Zero",
1: "One",
2: "Two",
3: "Three",
4: "Four",
5: "Five",
6: "Six",
7: "Seven",
8: "Eight",
9: "Nine"
]
let numbers: [Int] = [16, 58, 510]
// ["OneSix", "FiveEight", "FiveOneZero"] 배열 만들기
let strings: [String] = numbers.map { (number: Int) -> String in
// 매개변수로 가져온 number는 상수이기 때문에, 변수로 다시 만들어줘야 변경 가능하다 - 쉐도우 변수
var number: Int = number
var output: String = ""
repeat {
// print("\(number % 10)")
// number % 10 문구는 number를 10으로 나눈 나머지, 그러니까 소숫점 앞 맨 아랫자리수 한자리
// 앞자리에 해당하는 문자열을 계속 앞에 더해주는 문자열 결합
// output = digitNames[number % 10]! + output
// !처리는 피하고 싶어요
/*
if let name = digitNames[number % 10] {
output = name + output
} else {
output = "?" + output
}
*/
// 짧게 쓰는 옵셔널 바인딩의 또 다른 방법
let name = digitNames[number % 10] ?? "?"
output = name + output
// number는 Int이기 때문에, 10으로 나누면 소숫점 이하의 값은 내림처리한다.
number /= 10
} while number > 0
return output
}
print("\(strings)")
// 캡처값 예제
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal: Int = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
// 생성된 반환되는 함수는 runningTotal = 0,amount = 10으로 캡쳐되어 작동
let increFuncTen = makeIncrementer(forIncrement: 10)
// 생성된 반환되는 함수는 runningTotal = 0,amount = 7로 캡쳐되어 작동
let increFuncSeven = makeIncrementer(forIncrement: 7)
print("\(increFuncTen())") // 0 + 10 = 10
print("\(increFuncSeven())") // 0 + 7 = 7
print("\(increFuncTen())") // 10 + 10 = 20
print("\(increFuncSeven())") // 7 + 7 = 14
print("\(increFuncTen())") // 20 + 10 = 30
print("\(increFuncSeven())") // 14 + 7 = 21
// 두 함수의 관계는 참조 타입이라는 걸 알아보려 한다.
// 동일한 runningTotal 값을 공유하고 있다.
let alsoIncreFuncTen = increFuncTen
print("\(increFuncTen())") // 30 + 10 = 40
print("\(alsoIncreFuncTen())") // 0 + 10 = 10 ? -> 실제로는 50
print("\(increFuncTen())") // 40 + 10 = 50 ? -> 실제로는 60
// 필요하면 새로 만들어야...
let myIncreFuncTen = makeIncrementer(forIncrement: 10)
print("\(myIncreFuncTen())") // 0 + 10 = 10
// 이스케이프 클로저 (Escaping Closures) 예제
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosures(completionHandler: @escaping () -> Void ) {
completionHandlers.append(completionHandler)
}
func someFunctionWithNonescapingClosures(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 0
func doSomething() {
someFunctionWithEscapingClosures { () -> Void in
print("Hello")
self.x = 100
}
someFunctionWithNonescapingClosures { () -> Void in
print("World")
x = 200
}
}
}
let instance = SomeClass()
instance.doSomething()
print("someFunctionWithNonescapingClosures - x : \(instance.x)") // 당연히 200
if let completionHandler = completionHandlers.first {
completionHandler()
print("someFunctionWithEscapingClosures - x : \(instance.x)") // 200이 아니라 100
}
// mutating을 알아보자
class SomeClass {
var name: String = ""
func changeName(newName: String) {
self.name = newName
}
}
var someThingByClass = SomeClass()
someThingByClass.changeName(newName: "ned")
print("someThingByClass.name = \(someThingByClass.name)")
struct SomeStruct {
var name: String = ""
// 이 메소드에 의해서 프로퍼티가 바뀔 수 있음을 알리기 위해 mutating을 앞에 붙인다
mutating func changeName(newName: String) {
self.name = newName
}
}
var someThingByStruct = SomeStruct()
someThingByStruct.changeName(newName: "뽀뽀로")
print("someThingByStruct.name = \(someThingByStruct.name)")