일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- radeon vega
- 암드
- Zen2
- 젠2
- 라데온 신제품
- 베가
- Radeon
- 라데온 베가
- vega
- navi
- 라이젠
- 암드 신제품
- ryzen gen2
- 암드 라이젠 오버클럭
- Ryzen
- AMD
- Rx5700
- 라데온
- 라데온 나비
- 젠2 오버클럭
- ryzen zen2
- 라이젠 오버
- 라이젠 젠2
- 나비
- 라이젠 젠2 오버
- vega 7
- 라이젠 오버클럭
- Rx5000
- Today
- Total
SW
클로저 탈출 본문
탈출 클로저(Escaping Closure)
탈출(Escape) 클로저 ? : 함수의 전달인자로 전달한 클로저가 함수 종료 후에 호출될 경우 클로저가 함수를 탈출한다.
클로저를 매개변수로 갖는 함수를 선언할 때 매개변수 이름의 콜론(:) 뒤에 @escaping 키워드를 사용하여 클로저가 탈출하는 것을 허용한다고 명시해줄 수 있다.
예를 들어 비동기 작업을 실행하는 함수들은 클로저를 Completion handler 전달인자로 받아온다. 비동기 작업으로 함수가 종료되고 난 후 작업이 끝나고 호출할 필요가 있는 클로저를 사용해야 할 경우에 탈출 클로저(Escaping Closure)가 필요하다.
! @escaping 키워드를 따로 명시하지 않는다면 매개변수로 사용되는 클로저는 기본적으로 비탈출 클로저이다, 비탈출 클로저는 함수의 동작이 끝난 후 전달된 클로저가 필요 없을 때 사용한다.
typealias VoidVoidClosure = () -> Void
let firstClosure: VoidVoidClosure = {
print("Closure A")
}
let secondClosure: VoidVoidClosure = {
print("Closure B")
}
func returnOneClousure(first: @escaping VoidVoidClosure, second: @escaping VoidVoidClosure, shouldReturnFirstClosure: Bool) -> VoidVoidClosure{
return shouldReturnFirstClosure ? first : second
}
let returnedClosure: VoidVoidClosure = returnOneClousure(first: firstClosure, second: secondClosure, shouldReturnFirstClosure: true)
returnedClosure()
var closures: [VoidVoidClosure] = []
func appendClosure(closure: @escaping VoidVoidClosure) {
closures.append(closure)
}
typealias VoidVoidClosure = () -> Void
func functionWithNoescapeCloosure(closure: VoidVoidClosure) {
closure()
}
func functionWithEscapingClosure(completionHandler: @escaping VoidVoidClosure) -> VoidVoidClosure {
return completionHandler
}
class SomeClass {
var x = 10;
func runNoescapeClosure() {
functionWithNoescapeCloosure {
x = 200
}
}
func runEscapingClosure() -> VoidVoidClosure {
return functionWithEscapingClosure {
self.x = 100
}
}
}
let instance: SomeClass = SomeClass()
instance.runNoescapeClosure()
print(instance.x) //200
let returnedClosure: VoidVoidClosure = instance.runEscapingClosure()
returnedClosure()
print(instance.x) //100
비탈출 클로저나 탈출 클로저의 여러 가지의 상황 중 한 가지 애매한 경우가 있다.
비탈출 클로저로 전달한 클로저가 탈출 클로저인 척을 해야 하는 경우이다. 실제로는 탈출하지 않는데 다른 함수에서 탈출 클로저를 요구하는 상황에 해당 된다.
다음 에제에서 구현한 함수 hasElements(in:match: ) 는 in 매개변수로 검사할 배열을 전달받으며, match라는 매개변수로 검사를 실행할 클로저를 받아들인다.
hasElements(in:match:) 함수는 @escaping 키워드가 없으므로 비탈출 클로저를 전달받게 된다. 그리고 내부에서 배열 lazy 컬렉션에 있는 filter 메서드의 매개변수로 비탈출 클로저를 전달한다. 그런데 lazy 컬렉션은 비동기 작업을 할 때 사용하기 때문에 filter 메서드가 요구하는 클로저는 탈출 클로저 이다.
그래서 탈출 클로저 자리에 비탈출 클로저를 전달할 수 없다는 오류와 마주한다.
그런데 함수 전체를 보면 match 클로저가 탈출할 필요가 없다. 이때 해당 클로저를 탈출 클로저인 것 처럼 사용할 수 있게 돕는 withoutActuallyEscaping(_:do:)함수가 있다.
let numbers: [Int] = [2, 4, 6, 8]
let evenNumberPredicate = {(number : Int) -> Bool in
return number % 2 == 0
}
let oddNumberPredicate = {(number : Int) -> Bool in
return number % 2 != 0
}
func hasElements(in array: [Int], match predicate: (Int) -> Bool) -> Bool {
return withoutActuallyEscaping(predicate, do: {escapablePredicate in
return (array.lazy.filter {escapablePredicate($0)}.isEmpty == false)
})
}
let hasEvenNumber = hasElements(in: numbers, match: evenNumberPredicate)
let hasOddNumber = hasElements(in: numbers, match: oddNumberPredicate)
print(hasEvenNumber)
print(hasOddNumber)
withoutActuallyEscaping(_do:) 함수의 첫 번째 전달인자로 탈출 클로저인 척해야 하는 클로저가 전달되었다. do 전달인자는 이 비탈출 클로저를 또 매개변수로 전달받아 실제로 작업을 실행할 클로저를 전달합니다. 이렇게 withoutActuallyEscaping(_:do:) 함수를 활용하여 비탈출 클로저를 탈출 클로저 처럼 사용할 수 있다.
'프로그래밍 > Swift' 카테고리의 다른 글
옵셔널 이해하기 (복습) (0) | 2018.10.21 |
---|---|
자동 클로저 (학습) (0) | 2018.10.17 |
클로저 값 획득 (복습) (0) | 2018.10.17 |