[swift] 05. 클로저(Closure)

인라인 클로저 표현

{ (parameters) -> return type in
    statements
}

reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})

추론

// 인자 타입 생략
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )


// 단일 표현에서의 return keyword 생략
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )


// 이름, in 생략
reversedNames = names.sorted(by: { $0 > $1 } )


// String 끼리의 비교는 비교 연산자 (>)가 사용 가능하므로 아래처럼 축약 가능
reversedNames = names.sorted(by: >)

후위 클로저 (Trailing Closure)

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // function body goes here
}

// 마지막 인자인 경우, 인자값, 반환 값 생략 가능 
someFunctionThatTakesAClosure(closure: {
    // closure's body goes here
})

someFunctionThatTakesAClosure() {
    // trailing closure's body goes here
}
reversedNames = names.sorted() { $0 > $1 }

// 마지막 인자가 클로저이고 인자가 없다면 후위 클로저를 사용할 수 있다.
reversedNames = names.sorted { $0 > $1 }

캡쳐 

캡쳐한 값은 반환한 클로저의 생명주기와 같이한다. 새로이 생성될 때 캡쳐는 새로운 변수에 할당되고 이전에 생성한 클로저에는 영향을 주지 않는다. 

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}


let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen()
// 값으로 10을 반환합니다.
incrementByTen()
// 값으로 20을 반환합니다.
incrementByTen()
// 값으로 30을 반환합니다.


// 새로운 함수를 생성하면,
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7 <- 이전 값 30에 영향을 받지 않는다. 


incrementByTen()
// 값으로 40을 반환합니다. incrementBySeven 으로 생성한 값에 영향을 받지 않는다. 

Escaping 클로저

클로저가 함수 밖에서 실행되는 경우 (비동기 처리 등)는 클로저 파라미터 앞에 @escaping이라는 키워드를 명시해야 한다.  이 때 escaping 클로저에서는 self를 명시적으로 언급해야 한다.

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

자동클로저 (Autoclosures)

인자 값이 없으며 특정 표현을 감싸서 다른 함수에 전달 인자로 사용할 수 있는 클로저.

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"
// 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 사용
func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}

serve(customer: customersInLine.remove(at: 0)) // @autoclosure 를 선언해서 { } 생략 가능