don't stop believing

탈출 클로저 @escaping 본문

Swift/Basic

탈출 클로저 @escaping

Tongchun 2018. 1. 31. 15:48

함수의 전달인자로 전달한 클로저가 함수 종료 후에 호출도리 때 클로저가 함수를 탈출(Escape)한다고 표현합니다. 클로저를 매개변수로 갖는 함수를 선언할 때 매개 변수 이름의 콜론(:) 뒤에 @escaping 키워드를 사용하여 클로저가 탈출하는 것을 허용한다고 명시해줄 수 있습니다.


예를 들어 비동기 작업을 실행하는 함수들은 클로저를 컴플리션 핸들러(Completion handler) 전달인자로 받아옵니다. 비동기 작업으로 함수가 종료되고 난 후 작업이 끝나고 호출할 필요가 있는 클로져를 사용해야 할 때 탈출 클로조(Escaping Closure)가 필요합니다.


클로저가 함수를 탈출할 수 이는 경우 중 하나는 함수 외부에 정의된 변수나 상수에 저장되어 함수가 종료된 후에 사용할 경우입니다. 예를 들어 비동기로 작업을 하기 위해서 Completion handler를 전달인자를 이용해 클로저 형태로 받는 함수들이 많습니다. 함수가 작업을 종료하고 난 이후(즉, 함수의 return 후)에 Completion handler, 즉 클로저를 호출하기 때문에 클로저는 함수를 탈출해 있어야만 합니다. 함수의 전달인자로 전달받은 클로저를 다시 반환할 때도 마찬가지 입니다.


perfect-redis에서는 아래와 같이 Escaping Closure가 사용됩니다.

func redisSet(key: String, value: String, completion: @escaping (_ getValue: String) -> Void) {
    RedisClient.getClient(withIdentifier: RedisSessionConnector.connect()) { cli in
        do { let client = try cli()
            client.set(key: key, value: .string(value)) { response in
                guard case .simpleString(let s) = response else {
                    return
                }
                client.get(key: key) {response in
                    defer {
                        RedisClient.releaseClient(client)
                    }
                    guard case .bulkString = response else {
                        return
                    }
                    let returnValue = response.toString() ?? ""
                    // set 결과로 받은 returnValue를 completion으로 탈출 시킵니다.
                    completion(returnValue)
                }
            }
        } catch {
            print("Redis Error (redisSet):\(error)")
        }
    }
}

Escaping Closure의 호출은 아래와 같습니다.

func test(request: HTTPRequest, response: HTTPResponse) {
    do {
        let key = "dejavu"
        let value = "tongchun"
        var returnData = [String: Any]()

        // redisSet() 함수의 completion으로 return된 데이터를 getValue를 받습니다.
        rs.redisSet(key: key, value: value) { (getValue) in
            print("result set : \(getValue)")
            returnData = ["message": "redisSet result is \(getValue)"] as [String: Any]
        }

        comm.returnMessage(result: 0, data: returnData, response: response)

    } catch {
        response.setBody(string: "Error handling request: \(error)")
            .completed(status: .internalServerError)
    }
}


'Swift > Basic' 카테고리의 다른 글

Filter 사용하기  (0) 2018.02.19
Map 사용하기  (0) 2018.02.19
defer (후처리)  (0) 2018.01.24
Swift Access Control  (0) 2018.01.19
SPM에서 framework와 실행부분 코드 분리하기  (0) 2017.09.15
Comments