Concurrency 동시성
오늘은 동시성 Concurrecny에 대해서 공부해보자
기존의 비동기 작업에 대한 handling을 completionHandler라고 불리는 클로저로 해당 시점을 알려주는 방식으로 사용했습니다.
그런데 클로저 방식의 단점 몇가지 있는데
- completion을 호출하기 전에 에러 발생시 종료됨
- 클로저 방식이 중첩적으로 이루어져 콜백 지옥에 빠짐
이런 문제를 해결하기 위해서 async & await를 도입하게 되었습니다
이 방법을 사용하면 비동기 작업을 하더라도 Completion Handler 없이 호출되는 곳을 보장할 수 있습니다.
async
func fetchThumbnail(for index: Int) async throws -> UIImage {
함수 이름 뒤에 async가 붙으면 이 함수는 비동기라는 것을 나타냅니다
await
아까 만든 fetchThumbnail 함수를 사용하기 위해서는 await 키워드를 호출되는 지점 앞에 사용해야 됩니다.
let image = try await fetchThumbnail(for: index)
await로 표시된 곳은 잠재적인 일시 중단 지점으로 지정됩니다.
func fetchThumbnail(for index: Int) async throws -> UIImage {
let request = thumbnailURLRequest(for: index)
let (data, response) = try await URLSession.shared.data(for: request)
guard let statusCode = (response as? HTTPURLResponse)?.statusCode,
(200...299).contains(statusCode) else { throw FetchError.badID }
let maybeImage = UIImage(data: data)
guard let thumbnail = await maybeImage?.thumbnail else { throw FetchError.badImage }
return thumbnail
}
extension UIImage {
var thumbnail: UIImage? {
get async {
let size = CGSize(width: 160, height: 90)
return await self.byPreparingThumbnail(ofSize: size)
}
}
}
fetchThumbnail 함수는 이미지 데이터를 다운로드 받을 때까지 기다려야 하는 코드입니다.
내부에는 네트워크 통신을 하기 위해서 urlSession을 구현해 놓은 상태이고 urlSession으로 데이터를 가져올 때, 이미지 데이터의 사이즈를 변환하는 (시간이 오래 걸리는) 작업을 위해서 앞에 await 키워드가 명시되어 있습니다.
따라서 fetchThumbnail는 await를 사용하기 때문에 async 키워드를 명시하게 됩니다. 이 함수는 비동기라는 것을 나타내기 위해서 입니다.
그럼 다시 이 함수를 호출하는 쪽은 await를 붙이게 되고 무한 반복이 나타납니다.
아래는 async 키워드를 붙이지 않은 상태에서 오류가 나타나는 화면입니다.

async 코드는 동시 컨텍스트에서만 실행 가능합니다
다른 async 함수 내에서, 또는 Task {} 를 통해 수동으로 concurrent context를 제공할 때 그 안에서만 사용이 가능합니다.
// image 데이터를 가져오기 위한 액션 버튼
@IBAction func touchUpLoadImageButton(_ sender: UIButton) {
let index = sender.tag
resetImageView(at: index)
Task {
let image = try await fetchThumbnail(for: index)
self.imageViews[index].image = image
}
}
이런 방법으로 async & await를 Task 안에 처리하게 되면 task를 실행하는 함수에는 async 키워드를 붙이지 않는 것을 알 수 가있습니다!
참고:
'iOS' 카테고리의 다른 글
[iOS] CompositionalLayout을 한번 사용해보자 #2 (0) | 2024.05.18 |
---|---|
[iOS] CompositionalLayout을 한번 사용해보자 #1 (0) | 2024.05.13 |
[iOS] 새싹 프로젝트 (BoxOffice STEP3) #2 CollectionView 구현 (0) | 2024.03.24 |
[iOS] 새싹 프로젝트 (BoxOffice STEP3) #1 코드 베이스 화면 작업 (2) | 2024.03.15 |
[iOS] Hashable (0) | 2024.03.03 |
Concurrency 동시성
오늘은 동시성 Concurrecny에 대해서 공부해보자
기존의 비동기 작업에 대한 handling을 completionHandler라고 불리는 클로저로 해당 시점을 알려주는 방식으로 사용했습니다.
그런데 클로저 방식의 단점 몇가지 있는데
- completion을 호출하기 전에 에러 발생시 종료됨
- 클로저 방식이 중첩적으로 이루어져 콜백 지옥에 빠짐
이런 문제를 해결하기 위해서 async & await를 도입하게 되었습니다
이 방법을 사용하면 비동기 작업을 하더라도 Completion Handler 없이 호출되는 곳을 보장할 수 있습니다.
async
func fetchThumbnail(for index: Int) async throws -> UIImage {
함수 이름 뒤에 async가 붙으면 이 함수는 비동기라는 것을 나타냅니다
await
아까 만든 fetchThumbnail 함수를 사용하기 위해서는 await 키워드를 호출되는 지점 앞에 사용해야 됩니다.
let image = try await fetchThumbnail(for: index)
await로 표시된 곳은 잠재적인 일시 중단 지점으로 지정됩니다.
func fetchThumbnail(for index: Int) async throws -> UIImage {
let request = thumbnailURLRequest(for: index)
let (data, response) = try await URLSession.shared.data(for: request)
guard let statusCode = (response as? HTTPURLResponse)?.statusCode,
(200...299).contains(statusCode) else { throw FetchError.badID }
let maybeImage = UIImage(data: data)
guard let thumbnail = await maybeImage?.thumbnail else { throw FetchError.badImage }
return thumbnail
}
extension UIImage {
var thumbnail: UIImage? {
get async {
let size = CGSize(width: 160, height: 90)
return await self.byPreparingThumbnail(ofSize: size)
}
}
}
fetchThumbnail 함수는 이미지 데이터를 다운로드 받을 때까지 기다려야 하는 코드입니다.
내부에는 네트워크 통신을 하기 위해서 urlSession을 구현해 놓은 상태이고 urlSession으로 데이터를 가져올 때, 이미지 데이터의 사이즈를 변환하는 (시간이 오래 걸리는) 작업을 위해서 앞에 await 키워드가 명시되어 있습니다.
따라서 fetchThumbnail는 await를 사용하기 때문에 async 키워드를 명시하게 됩니다. 이 함수는 비동기라는 것을 나타내기 위해서 입니다.
그럼 다시 이 함수를 호출하는 쪽은 await를 붙이게 되고 무한 반복이 나타납니다.
아래는 async 키워드를 붙이지 않은 상태에서 오류가 나타나는 화면입니다.

async 코드는 동시 컨텍스트에서만 실행 가능합니다
다른 async 함수 내에서, 또는 Task {} 를 통해 수동으로 concurrent context를 제공할 때 그 안에서만 사용이 가능합니다.
// image 데이터를 가져오기 위한 액션 버튼
@IBAction func touchUpLoadImageButton(_ sender: UIButton) {
let index = sender.tag
resetImageView(at: index)
Task {
let image = try await fetchThumbnail(for: index)
self.imageViews[index].image = image
}
}
이런 방법으로 async & await를 Task 안에 처리하게 되면 task를 실행하는 함수에는 async 키워드를 붙이지 않는 것을 알 수 가있습니다!
참고:
'iOS' 카테고리의 다른 글
[iOS] CompositionalLayout을 한번 사용해보자 #2 (0) | 2024.05.18 |
---|---|
[iOS] CompositionalLayout을 한번 사용해보자 #1 (0) | 2024.05.13 |
[iOS] 새싹 프로젝트 (BoxOffice STEP3) #2 CollectionView 구현 (0) | 2024.03.24 |
[iOS] 새싹 프로젝트 (BoxOffice STEP3) #1 코드 베이스 화면 작업 (2) | 2024.03.15 |
[iOS] Hashable (0) | 2024.03.03 |