Swiftful Thinking 이라는 유튜브 채널을 보며 Concurrency를 공부해봤습니다.
공부하면서 알아두면 좋겠다는 생각이 든 요소들을 공유하고자 합니다.
해당 영상은 아래 링크를 통해 볼 수 있으며, 비동기 처리, 그중에서도 async에 관심이 있다면 한번쯤 보시는 것을 추천드립니다!
// throw를 통해 이 함수를 쓰는 부분에서 에러를 처리하게 함
func getTitle() throws -> String {
if isActive {
return "NEW TEXT!"
} else {
throw URLError(.badURL)
}
}
do {
// 에러가 날 경우 nil 값 반환, catch로 안빠짐
let optionalTitle = try? manager.getTitle()
if let optionalTitle = optionalTitle {
self.text = optionalTitle
}
// 에러가 날 경우 catch로 빠짐
let catchTitle = try manager.getTitle()
self.text = catchTitle
} catch {
self.text = error.localizedDescription
}
// Escaping Closure
func downloadWithEscaping(completionHandler: @escaping ( _ image: UIImage?, _ error: Error?) -> ()) {
URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
let image = self?.handleResponse(data: data, response: response)
completionHandler(image, nil)
}
.resume()
}
// Combine
func downloadWithCombine() -> AnyPublisher<UIImage?, Error> {
URLSession.shared.dataTaskPublisher(for: url)
.map(handleResponse)
.mapError({ $0 })
.eraseToAnyPublisher()
}
// Async
func downloadWithAsync() async throws -> UIImage? {
do {
// 다운 받을때까지 이 코드에서 흐름이 잠깐 멈춤
let (data, response) = try await URLSession.shared.data(from: url)
return handleResponse(data: data, response: response)
} catch {
throw error
}
}
// 이미지 다운받는 비동기 처리
func fetchImage() async {
do {
...
let (data, _) = try await URLSession.shared.data(from: url)
...
} catch {
print(error.localizedDescription)
}
}
// Task 안에 있는 코드들은 순서대로 진행됨
Task {
await viewModel.fetchImage()
await viewModel.fetchImage()
}
// 다른 Task와는 별개로 실행됨
Task {
await viewModel.fetchImage()
await viewModel.fetchImage()
}
// Task에 우선순위를 지정 가능함
Task(priority: .high) {
...
}
// Task를 취소할 수 있음
let myTask = Task(priority: .userInitiated) {
...
}
myTask.cancel()
/*
// 차례대로 기다리면서 실행함 -> 모든 비동기 처리를 동시에 하고 싶으면?
Task {
do {
let image1 = try await fetchImage()
self.images.append(image1)
let image2 = try await fetchImage()
self.images.append(image2)
let image3 = try await fetchImage()
self.images.append(image3)
let image4 = try await fetchImage()
self.images.append(image4)
} catch { }
}
*/
Task {
do {
// async let 으로 함수를 정의하고, await로 묶기
async let fetchImage1 = fetchImage()
async let fetchImage2 = fetchImage()
async let fetchImage3 = fetchImage()
async let fetchImage4 = fetchImage()
// 네가지 비동기 처리가 모두 끝날때까지 기다렸다가 다음 코드를 실행하기
let (image1, image2, image3, image4) = await (try fetchImage1, try fetchImage2, try fetchImage3, try fetchImage4)
self.images.append(contentsOf: [image1, image2, image3, image4])
} catch { }
}
func fetchImageWithTaskGroup() async throws -> [UIImage] {
return try await withThrowingTaskGroup(of: UIImage?.self) { group in
var images: [UIImage] = []
for urlString in urlStrings {
// 처리하고 싶은 Task들을 추가함
group.addTask {
// 실패하면 나머지 비동기 처리도 끊김
// try await self.fetchImage(urlString: urlString)
// 실패해도 그 비동기 처리만 nil 반환함
try? await self.fetchImage(urlString: urlString)
}
}
// 일반적인 for문이 아님
// 모든 task가 끝날때까지 기다리는 for문임
for try await image in group {
if let image = image {
images.append(image)
}
}
return images
}
}
// completion handler를 썼을 경우
func getHeartImageFromDatabase(completionHandler: @escaping (_ image: UIImage) -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now()+5) {
completionHandler(UIImage(systemName: "heart.fill")!)
}
}
// continuation을 썼을 경우 (async await)
func getHeartImageFromDatabase() async -> UIImage {
await withCheckedContinuation { continuation in
DispatchQueue.main.asyncAfter(deadline: .now()+5) {
continuation.resume(returning: UIImage(systemName: "heart.fill")!)
}
}
}
...
.task {
self.image = await getHeartImageFromDatabase()
}
// class와 거의 같음
// + thread-safe 특성을 가지고 있음 (await)
actor MyActor {
var title: String
init(title: String) {
self.title = title
}
func updateTitle(newTitle: String) {
title = newTitle
}
// await가 필요없음
nonisolated func updateTitleWithoutWait(newTitle: String) {
title = newTitle
}
}
private func actorTest1() {
Task {
let object = MyActor(title: "first title")
// actor 프로퍼티에 접근할때마다 await 해줘야함
await print("Object: ", object.title)
await object.updateTitle(newTitle: "second title")
await print("Object: ", object.title)
object.updateTitleWithoutWait(newTitle: "third title")
}
}
많이 쓰일 것 같은 문법에 대해서만 설명을 했지만, 실제 영상에는 좀 더 다양하고 어려운 문법에 대해 설명해주십니다.
관심이 있다면 한번 봐보시면 좋을 듯 합니다!