천원의 개발

iOS Continuation 정리 본문

iOS&Swift🍎/iOS

iOS Continuation 정리

천 원 2023. 11. 24. 10:21

안녕하세요. 천원입니다.

오늘은 CompletionHandler로 값을 리턴하는 함수들을 async await 스타일로 변경할 때 사용하는 Continuation에 대하여 학습해 보겠습니다.

공식 문서를 보면 CheckedContiuationUnsafeContination 두 종류가 있는데 한번 확인해 보겠습니다.

 

 

 

 

 

 

 

 

 

동기와 비동기 코드의 사이를 인터페이스하는 메커니즘으로, 정확성 위반을 기록한다고 하네요.

 

 

 

 

 

이친구는 정확성을 검사하지 않는다고 하네요.

 

 

 

 

 

 

 

 

 

 

이제 코드를 보자면 dataTask함수의 completionHandler를 통해서 받은 data를 다시 handler에 실어서 보내주는 코드입니다.

func requestImage(handler: @escaping ((Data) -> Void)) {
    let request = URLRequest(url: URL(string: url)!)
    URLSession.shared.dataTask(with: request) { data, response, error in
        handler(data!)
    }.resume()
}

requestImage { data in
    print(data)
}

 

 

 

 

 

 

 

 

 

이러한 코드를 async await 문법으로 변경해 볼텐데 아래와 같이 CheckedContinuation을 사용하는 함수 withCheckedContinuationwithCheckedThrowingContinuation이 존재하는데 차이점을 보면 이름처럼 error를 throw 할때는 withCheckedThrowingContinuation를 사용하는 것 같네요.

 

 

 

 

 

 

 

 

 

먼저 withCheckedContinuation를 사용해 보겠습니다.

func requestImageAsycAwait() async -> Data {
    let request = URLRequest(url: URL(string: url)!)

    return await withCheckedContinuation { continuation in
        URLSession.shared.dataTask(with: request) { data, response, error in
            continuation.resume(returning: data!)

        }.resume()
    }
}

Task {
    let data = await requestImageAsycAwait()
}

 

dataTask 함수를 withCheckedContinuation Handler 내부에 위치시켜 data를 resume 함수로 리턴하는 모습입니다.

 

 

 

 

 

continuation의 구체적인 타입을 확인해보면 Data와 Never를 제네릭으로 가지고 있는걸 보니 에러를 핸들링 하지 않기 때문에 Never가 들어간 모습입니다. 

 

 

 

 

 

 

 

 

 

 이번에는 withCheckedThrowingContinuation을 사용해 보면

func requestImageAsycAwait() async throws -> Data {
    let request = URLRequest(url: URL(string: url)!)

    return try await withCheckedThrowingContinuation { continuation in
        URLSession.shared.dataTask(with: request) { data, response, error in
            if let data {
                continuation.resume(returning: data)
            } else if let error {
                continuation.resume(throwing: error)
            }
        }.resume()
    }
}

Task {
    do {
        let data = try await requestImageAsycAwait()
    } catch {
        print(error)
    }
}

 

더 안정적인 코드가 작성이 되었습니다.

 

 

 

 

 

 

 

 

 

다음으로 UnsafeContinuation을 간단하게 보자면 CheckedContiuation이랑 사용법은 완전히 동일하지만 Continuation을 사용할 때는 resume을 호출하지 않으면 코드가 영원히 await 상태에 머물게 되고 이는 리소스 낭비로 이어지게 되는데, CheckedContiuation을 사용하면 콘솔창에 명시적으로 leaked되고 있음을 알려주지만

 

 

 

 

 

 

UnsafeContinuation를 사용하게 되면 아무런 에러가 발생하지 않는 모습입니다. UnsafeContinuation는 오버헤드가 적은 반면에 사용 시에 주의가 필요하겠네요. 

 

 

출처: 

https://developer.apple.com/documentation/swift/checkedcontinuation