[Swift Concurrency] Task and .task

Woozoo·2023년 4월 18일
0

[Swift Concurrency]

목록 보기
4/13
class TaskBootcampViewModel: ObservableObject {
    @Published var image: UIImage? = nil
    @Published var image2: UIImage? = nil
    
    func fetchImage() async {
        do {
            guard let url = URL(string: "https://picsum.photos/1000") else { return }
            let (data, _) = try await URLSession.shared.data(from: url, delegate: nil)
            self.image = UIImage(data: data)
        } catch {
            print(error.localizedDescription)
        }
    }
    
    func fetchImage2() async {
        do {
            guard let url = URL(string: "https://picsum.photos/1000") else { return }
            let (data, _) = try await URLSession.shared.data(from: url, delegate: nil)
            self.image2 = UIImage(data: data)
        } catch {
            print(error.localizedDescription)
        }
    }
}

요런 뷰모델이 있다고 해봅시다

struct TaskBootcamp: View {
    @StateObject private var viewModel = TaskBootcampViewModel()
    
    var body: some View {
        VStack(spacing: 40) {
            if let image = viewModel.image {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
                    .frame(width: 200, height: 200)
            }
            if let image = viewModel.image2 {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
                    .frame(width: 200, height: 200)
            }
        }
        .onAppear {
            Task {
                await viewModel.fetchImage()
                await viewModel.fetchImage2()
            }
        }
    }
}

그리고 이런 뷰가 있다고해봅시다!
onAppear일 때 fetch 하고 있는데
await로 처리하고 있어서 첫번째 이미지가 다 불러와진 후에
image2가 화면에 뜹니다

Task 안에서 await가 위아래로 있으면 위에 로직이 끝나고 다음 로직이 실행된다!
(한 Task 안에서 같이 로딩할 방법도 있긴함! 이건 담시간에)

이런 상황일 때 간단히 처리하는 방법은
Task 블록은 두개 따로따로 작성해주는 거!

오케이?

그럼 만약에 2개 이상의 Task가 있을 경우에 처리되는 순서는 어떻게 될까?



똑같다?!


사실 Task 만들 때 priority 설정해줄 수 있음

high : <_NSMainThread: 0x600003bcc100>{number = 1, name = main} : TaskPriority(rawValue: 25)
userInitiated : <_NSMainThread: 0x600003bcc100>{number = 1, name = main} : TaskPriority(rawValue: 25)
medium : <_NSMainThread: 0x600003bcc100>{number = 1, name = main} : TaskPriority(rawValue: 21)
background : <_NSMainThread: 0x600003bcc100>{number = 1, name = main} : TaskPriority(rawValue: 9)
LOW : <_NSMainThread: 0x600003bcc100>{number = 1, name = main} : TaskPriority(rawValue: 17)
utility : <_NSMainThread: 0x600003bcc100>{number = 1, name = main} : TaskPriority(rawValue: 17)

콘솔에 이렇게 찍히는 걸 볼 수 있었다



Task sleep 가능했었죠

만약에 특정한 시점에서 나 말고 다른 애들 priority를 먼저 높여주고 싶다!
Task.yield()라는 메소드 사용하면 됨



Task 안에 Task도 넣을 수는 있지만 지금처럼 작성할 경우엔 똑같은 priority다

child 태스크는 parent 태스크의 메타데이터를 다 물려받아서 그럼


detach로 떼버리면 priority도 달라짐! (근데 문서에서는 이거 웬만해선 사용하지 말라고함)


fetchImage 메소드 5초 동안 Task 잠들게 해줌

그리고 MainActor로 메인에서 이미지가 바뀔 수 있게 해주고!
이상태에서 NavigationLink로 새로운 뷰로 진입시에 이 비동기 메소드가 호출되게 해주면 5초 전에 뷰를 빠져나와도 백에서 작업이 취소가 안되서 프린트가 찍혀버림!

어떻게 해줘야할까?



Task 타입의 변수에 넣고 이걸
.onDisappear일 때 cancel 해주는겨


ios 15에서 새로 생긴 modifier는
.task { } modifier다!!


따로 Task 안만들고도 .task로 처리가 가능함

이러면 .onAppear일 때 , .onDisAppear일 때 따로 처리 안해줘도
알아서 뷰가 사라지면 task가 cancel 됩니다!!

근데 cancell이 된다고 해서 바로 즉시 cancel이 되는 것은 아니다
try Task.checkCancellation() 로 체크해보기


profile
우주형

0개의 댓글