공부 연습장 :-)

탈출 클로저 (@escaping closure)

|

@escaping

함수 사이에 실행 순서를 정하고 싶을때 Escaping Closure를 사용한다. 비동기 처리시 유용하다.

발견

  • 주소록앱 미션 중 연락처를 fetch해오는 과정에서 테이블뷰 화면이 표시되지 않는 문제 발생.
  • 하지만 디버깅해보니 연락처는 다 정상적으로 가져와짐.
  • 문제는 연락처를 request하는 함수가 비동기로 처리되는거라 연락처가 다 fetch되지 않은 상태에서 테이블뷰를 load했기 때문.
  • 연락처가 모두 fetch되는 비동기로 처리되는 동작이 모두 끝난 후 뷰를 다시 로드해야 했기 때문에 함수 사이에 실행순서를 보장하고 싶어서 escaping(탈출 클로저) 사용.

나만의 결론

  • 함수를 벗어나서도 클로저를 유효하게 사용(호출, 저장 - outLive)하고싶을 때 escaping을 사용한다!
  • escaping을 사용하면 A 함수가 마무리된 상태에서만 B 함수가 실행되도록 작성할 수 있다.
  • 이는 비동기로 실행되어 실행 직후 반환되어버리는 함수 동작과 관련하여 동작 순서를 정해줄때 매우매우 유용하다.

예시 코드

// 함수 외부에 클로저를 저장하는 예시

// 클로저를 저장하는 배열
var completionHandlers: [() -> Void] = []

/*
함수 밖에 있는 completionHandlers 배열에 해당 클로저를 저장.
즉, 클로저가 함수에서 빠져나감.
이렇게 함수를 호출하는 도중에 해당 함수 외부에 클로저를 저장하기 위해서는
Escaping Closure이어야 함.
*/

func withEscaping(completion: @escaping () -> Void) {
    completionHandlers.append(completion)
}

func withoutEscaping(completion: () -> Void) {
    completion()
}

class MyClass {
    var x = 10
    func callFunc() {
        withEscaping { self.x = 100 }
        // 만약 여기서 self키워드를 안쓰면 아래의 에러가 발생한다.
        // Reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit. Insert self.
        // 클로저에서 속성에 접근할때는 self.키워드 명시해야함
        withoutEscaping { x = 200 }
        // 근데 이것도 클로저 아닌가?
    }
}
let mc = MyClass()
mc.callFunc() // x=200코드가 withoutEscaping의 인자로 들어가서 그냥 실행되어버림
print(mc.x) // callFunc()를 호출하면서 변경된 x값 출력

completionHandlers.first?() // array에 저장된 첫번째 클로저 실행
print(mc.x) // callFunc()를 호출하면서 withEscaping이 실행되어서 클로저가 저장되었고,
            // array에 저장된 첫번째 클로저가 실행되면서 변경된 x=100 출력

// 결과
// 200
// 100

Comments