Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- swift documentation
- 스위프트
- Xcode
- 리펙토링
- UICollectionView
- combine
- RxCocoa
- rxswift
- uitableview
- swiftUI
- SWIFT
- Human interface guide
- ios
- clean architecture
- MVVM
- Clean Code
- 클린 코드
- Protocol
- 애니메이션
- Refactoring
- UITextView
- 리팩토링
- 리펙터링
- uiscrollview
- ribs
- Observable
- tableView
- map
- collectionview
- HIG
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] Async, Await 사용 방법 본문
Async, Await 이란?
- 기존에 비동기 처리 방식은 DispatchQueue나 completionHandler를 사용하여 처리했지만, 더욱 편하게 비동기 처리할 수 있는 문법
// DispatchQueue 사용한 비동기 처리
DispatchQueue.global.async {
}
// completionHandler를 사용한 비동기 처리
let task = URLSession.shared.dataTask(with: url, completionHandler: { data, response, error in
}).resume()
- 비동기 처리란 위와 같이 오래걸리는 작업이 있을 때, 그 작업이 끝나는 것을 기다리지 않고 수행하도록 일을 처리하는 것
- 이 밖에도 대표적인 비동기 처리 방법에는 RxSwift와 Combine가 존재
Async, Await 등장 배경 - call back 지옥
- 기존 completionBlock 문법은 장황한 코드를 발생
func processImageData1(completionBlock: (_ result: Image) -> Void) {
loadWebResource("dataprofile.txt") { dataResource in
loadWebResource("imagedata.dat") { imageResource in
decodeImage(dataResource, imageResource) { imageTmp in
dewarpAndCleanupImage(imageTmp) { imageResult in
completionBlock(imageResult)
}
}
}
}
}
processImageData1 { image in
display(image)
}
- 에러 핸들링도 복잡
func processImageData2c(completionBlock: (Result<Image, Error>) -> Void) {
loadWebResource("dataprofile.txt") { dataResourceResult in
switch dataResourceResult {
case .success(let dataResource):
loadWebResource("imagedata.dat") { imageResourceResult in
switch imageResourceResult {
case .success(let imageResource):
decodeImage(dataResource, imageResource) { imageTmpResult in
switch imageTmpResult {
case .success(let imageTmp):
dewarpAndCleanupImage(imageTmp) { imageResult in
completionBlock(imageResult)
}
case .failure(let error):
completionBlock(.failure(error))
}
}
case .failure(let error):
completionBlock(.failure(error))
}
}
case .failure(let error):
completionBlock(.failure(error))
}
}
}
processImageData2c { result in
switch result {
case .success(let image):
display(image)
case .failure(let error):
display("No image today", error)
}
}
- async와 await를 통해 코드를 복잡하지 않게 사용이 가능
- call back 지옥에서 벗어난 코드
func loadWebResource(_ path: String) async throws -> Resource
func decodeImage(_ r1: Resource, _ r2: Resource) async throws -> Image
func dewarpAndCleanupImage(_ i : Image) async throws -> Image
func processImageData() async throws -> Image {
let dataResource = try await loadWebResource("dataprofile.txt")
let imageResource = try await loadWebResource("imagedata.dat")
let imageTmp = try await decodeImage(dataResource, imageResource)
let imageResult = try await dewarpAndCleanupImage(imageTmp)
return imageResult
}
Async, Await를 처리하는 내부 원리 (스레드 관리)
- sync에서의 스레드 관리
- 호출 - A함수에서 B함수(sync)를 호출하면, A함수가 실행되던 스레드의 제어권을 B에게 전달
- 진행 - B함수가 끝날때까지 해당 스레드는 점유되어서 다른 일을 수행하지 않게되는 원리
- 종료 - B함수가 종료되면 A함수에게 다시 스레드 제어권을 반납
- async에서의 스레드 관리
- 호출 - A함수에서 B함수(async)를 호출하면, A함수가 실행되던 스레드의 제어권을 B에게 전달
- 진행 - B함수는 async이기 때문에 스레드의 제어권을 포기하는 suspend가 가능 (suspend되면 호출한 A함수도 같이 suspend됨)
- suspend - 스레드에 대한 제어권은 system으로 가고 시스템은 스레드를 사용하여 다른 작업을 수행
- resume - 일시 중단된 비동기 함수 B를 다시 실행하는 단계
- 종료 - B함수가 종료되면 A함수에게 스레드 제어권을 반납
Async, Await 사용 방법
예제 코드) 특정 url을 가지고 이미지 url를 획득 -> 이미지 url을 가지고 Data(contentsOf:)로 이미지 데이터 획득 -> UIImageView에 표출
- async, await를 사용하지 않은 경우
- URLSession에서 request
- response로 받아온 값을 가지고 디코딩하여 completion에 이미지 url을 넘겨줌
- 사용하는 쪽에서 이미지 url을 가지고 data 획득
- data를 가지고 이미지 뷰에 표출
@objc private func didTapButton() {
guard let url = URL(string: "https://reqres.in/api/users?page=2") else { return }
self.requestImageURL(requestURL: url) { [weak self] urlString in
DispatchQueue.global().async {
guard
let url = URL(string: urlString),
let data = try? Data(contentsOf: url)
else { return }
DispatchQueue.main.async {
self?.imageView.image = UIImage(data: data)
}
}
}
}
func requestImageURL(requestURL: URL, completion: @escaping (String) -> Void) {
URLSession.shared.dataTask(
with: requestURL,
completionHandler: { data, response, _ in
guard
let data = data,
let httpResponse = response as? HTTPURLResponse,
200..<300 ~= httpResponse.statusCode
else { return }
let decoder = JSONDecoder()
if let decoded = try? decoder.decode(MyModel.self, from: data) {
completion(decoded.data.first?.avatar ?? "")
}
}).resume()
}
- async, await를 사용할 경우
- 메소드 시그니처에 async를 붙여서 비동기 작업임을 알리는 키워드를 추가
- 메소드 내부 구현에서 비동기가 예상되는 곳에 await 키워드를 추가
- 해당 메소드를 호출하는 쪽에서는 async block에서 호출하고, await키워드를 붙여서 호출
@objc private func didTapButton() {
guard let url = URL(string: "https://reqres.in/api/users?page=2") else { return }
async {
guard
let imageURL = try? await self.requestImageURL(requestURL: url),
let url = URL(string: imageURL),
let data = try? Data(contentsOf: url)
else { return }
print(Thread.isMainThread) // true
self.imageView.image = UIImage(data: data)
}
}
func requestImageURL(requestURL: URL) async throws -> String {
print(Thread.isMainThread) // false
let (data, _) = try await URLSession.shared.data(from: requestURL)
return try JSONDecoder().decode(MyModel.self, from: data).data.first?.avatar ?? ""
}
* async 블록으로 감싸서 호출하는 경우, 아래처럼 deprecated 되었으므로 Task를 사용
Task { // <- async대신 Task 사용
guard
let imageURL = try? await self.requestImageURL(requestURL: url),
let url = URL(string: imageURL),
let data = try? Data(contentsOf: url)
else { return }
print(Thread.isMainThread) // true
self.imageView.image = UIImage(data: data)
}
* 전체 코드: https://github.com/JK0369/ExAsyncAwait
* 참고
https://developer.apple.com/videos/play/wwdc2021/10095/
https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md
'swift 5 문법' 카테고리의 다른 글
[iOS - swift] 예약어를 변수로 사용 (backtic, `기호) (0) | 2021.06.23 |
---|---|
[iOS - swift] (프로퍼티) Stored property vs Computed property vs Type property (0) | 2021.06.16 |
[iOS - swift] (초기화) designated init vs convenience init (0) | 2021.06.16 |
[swift] 19. struct vs. class (0) | 2020.10.01 |
[swift] 18. 초기화(initialize) 심화 개념 (0) | 2020.10.01 |
Comments