iOS 개발 기록

[Concurrency]Continuation 본문

iOS/비동기처리

[Concurrency]Continuation

택꽁이 2023. 4. 4. 17:17
728x90
📄

Continuation

  • Push를 등록을 설정하던 도중에 여러 콜백 함수로 인한 장풍이 생기고 뭔가 가독성이 거슬려서 전에 공부했던 async/await로 장풍 좀 없애고 싶다는 생각이 들었다.
  • 요즘엔 많은 메소드들이 async/await로 구현되어서 편하게 구현하고 있어서 편하게 변환하고 있던 중 막히는 순간이 왔다.
    /// 막혔던 예시 
    var pushTokenHandler: ((String?) -> Void)? = nil 
    
    func pushTokenRegister(handler: @escaping (_ fcmToken: String?) -> Void) { 
    	self.pushTokenHandler = handler
    } 
    
    /// 사용
    pushTokenRegister { fcmToken in 
    	print(fcmToken)
    }
  • 찾아보니 이런 비동기 코드들(Callback이나 Delegate 등)을 진행 상태를 async/await 함수와 같이 상황에 따라 일시 중지(suspend)재개(resume)상태로 돌릴 수 있는 인터페이스를 제공한다. 이를 Continuation이라 한다.

→ Continuation은 기존에 구현된 비동기 코드들을 wrapping하여 사용한다. → 위의 Thread 제어권에 대한 설명은 데브시스터즈 기술 블로그에 매우매우 잘 설명이 되어있다!

resume

  • await로 suspend 된 지점에서 다시 작업을 재개하는 메소드
  • 반환 값에 따라 resume(), resume(returning: T), resume(throwing: E), resume(with: Result<T, E>) 등을 제공한다.
  • ❗️Continuation에서 resume 메소드는 반드시 단 한번 실행되야 한다.

→ 실행되지 않으면 영원히 await, 2번 이상 실행되면 뭔 일이 일어날지 예상 못한다.

CheckedContinuation

  • 런타임에 resume 작업을 체크한다. resume이 누락되거나 여러개 사용된 경우 명시적으로 Fatal error를 발생시킨다.
  • 예외 처리에 따라 다음과 같은 메소드를 제공한다.
    • withCheckedContinuation(function: _: )
    • withCheckedThrowingContinuation<T>(function:_:)

UnsafeContinuation

  • resume를 체크하지 않는다. UnsafeContinuation은 event loops, delegate, callback을 낮은 오버헤드 매커니즘으로 처리하기 위해 만들어졌다.
  • 예외 처리에 따라 다음과 같은 메소드를 제공한다.
    • withUnsafeContinuation(_:)
    • withUnsafeThrowingContinuation<T>(_:)

→ 두 메소드 모두 다른 변경 없이 서로 대체할 수 있다.

코드

var pushTokenHandler: ((String?) -> Void)? = nil 

func pushTokenRegister(handler: @escaping (_ fcmToken: String?) -> Void) { 
	self.pushTokenHandler = handler
} 

/// 사용
pushTokenRegister { fcmToken in 
	print(fcmToken)
}
/// pushTokenRegister 
func pushTokenRegister() async -> String? {
	/// CheckedContinuation 사용
  return await withCheckedContinuation{ continuation in
		/// 기존의 pushTokenRegister
    pushTokenRegister { fcmToken in
			/// 해당 값을 continuation을 통해 resume
      continuation.resume(returning: fcmToken)
    }
  }
}

/// 사용
let fcmToken = await AppDelegate.shared.pushRegister()
  • 해당 예에서는 resume(returning: T) 를 통해 값만 보냈지만 실제로는 resume(with: Result<T, E>) 를 통해 에러를 담아 보내는게 분기처리에 유리해 자주 쓰일듯하다.

결론

  • 만들어보니 실제로 콜백으로 인한 들여쓰기가 줄어들고, 가독성도 편해진 것을 확인할 수 있었다.
  • 성능 테스트는 안해봤는데 suspend와 resume로 컨텍스트 스위칭을 주려 성능이 조금 나아지지 않았을까 …? 사실 기존 메소드를 wrapping 한거라 확실하진 않는데 성능차이도 실제로 생길지 궁금하다.

Reference

  • Swift Concurrency
Documentation
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/
  • checkedContinuation
CheckedContinuation | Apple Developer Documentation
A mechanism to interface between synchronous and asynchronous code, logging correctness violations.
https://developer.apple.com/documentation/swift/checkedcontinuation
  • Thread 제어권
바삭한 신입들의 동시성 이야기 - Swift편
Swift 5.5에 도입된 async, await 문법의 등장 배경과 동작 원리를 알아봅니다.
https://tech.devsisters.com/posts/crunchy-concurrency-swift/

Uploaded by N2T

'iOS > 비동기처리' 카테고리의 다른 글

[Concurrency] AsyncStream  (0) 2023.04.07
[Swift] CompletionHandler  (0) 2023.02.06
[Concurrency] async / await  (0) 2023.02.06