본문 바로가기

Programming/Swift

withCheckedContinuation memory leak 현상 및 해결방법

반응형

Handler를 async/await으로 바꾸려는 경우

핸들러에 withCheckedContinuation를 추가해 아래와 같이 사용하는데요


Class MessageManager {
  func recentMessages() async -> [String] {
    return await withCheckedContinuation { continuation in
      self.requestMessages() { list in
        continuration.resume(returning: list)
      }
    }
  }	
}


let messages = await recentMessages()

 

평상시에는 문제가 되지 않으나

만약 Class가 부득이하게 종료해야하는 상황이 오면, 기존에 요청했던 async를 모두 완료 되기 전에는 정상적으로 해제 할 수 없는 문제가 발생합니다. 메모리릭 현상이 발생할 수 있는 문제도 안고 있습니다.

 

이를 해결하기 위해서는 CheckedContinuation을 전역변수로 등록 후 관리하는 방법이 있습니다.

아래는 이를 구현한 예제입니다.

enum MessageManagerError: Error {
  case cancelRequest
}

Class MessageManager {
  var continuation: CheckedContinuation<[String], Error>?
    
  func recentMessages() async throws -> [String] {
    return try await withCheckedThrowingContinuation {	continuation in
      self.continuation = continuation
            
      self.requestMessages() { list in
        continuration.resume(returning: list)
      }
    }
  }
    
  func cancel() {
    continuation.resume(throwing: MessageManagerError.cancelRequest)
  }
}

 

CheckedContinuration을 관리하는 변수를 만들고 먼저 종료해야하는 경우 기존 continuation을 끝내주도록 작업해야합니다.

 

이 방법은 Delegate에서도 활용할 수 있습니다.

...

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    continuation.resume(returning: data)
}

func webView(WKWebView, didFail: WKNavigation!, withError: Error) {
    // our work failed
    continuation.resume(throwing: withError)
}

...

 

Delegate를 통한 설정방법은 아래 예시를 통해 확인 가능합니다.

https://www.hackingwithswift.com/quick-start/concurrency/how-to-store-continuations-to-be-resumed-later

 

반응형