
let queue = DispatchQueue(label: "testQueue")
main dispatch queue가 자동적으로 생성된다. 이는 UI를 담당하는 serial queue이다. let queue = DispatchQueue.global(qos: . userInteractive)
| 구분 | 사용해야 하는 상황 | 소요 시간 |
|---|---|---|
| .userInteractive | 유저와 직접 상호작용 하는 작업에 권장: UI 업데이트 계산, 애니메이션 또는 UI를 반응적이고 빠르게 유지하는 모든 것 | 거의 즉시 |
| .userInitiated | 유저가 UI에서 즉시 발생해야하지만 비동기적으로 수행될 수 있는 작업들: 앱 내부에서 pdf 파일을 열겨나, 로컬 데이터베이스 읽기 등 | 몇초 |
| .default | 일반적인 작업 | - |
| .utility | 보통 Progress Indicator와 함께 길게 실행되는 작업: 오랜 시간 동안 실행되는 시간, I/O, 네트워킹 또는 연속 데이터 피드 등 | 몇초에서 몇분 |
| .background | 유저가 직접적으로 인지하지 않고(시간이 안 중요한)작업: 미리 가져 오기, 데이터베이스 유지 관리, 원격 서버 동기와 및 백업 등 | 몇분이상 |
| .unspecified | legacy API 지원 | - |
async와 sync를 이용해 큐에 작업(task)을 추가할 수 있다. DispatchQueue.global(qos: .utility).async { [weak self] in
guard let self = self else { return }
// 작업 수행
// UI 업데이트를 위해 다시 메인 큐로 전환
DispatchQueue.main.async {
self.textLabel.text = "새로운 기사가 있습니다!"
}
}
DispatchQueue에는 클로저 규칙을 준수해야 한다. 따라서 클로저의 캡처된 변수를 올바르게 처리해야 한다.
self를 강하게 캡처하면 참조 사이클이 발생하지는 않는다. 해당 클로저가 완료되면 전체적으로 해제되기 때문이다. self를 강하게 캡쳐하면 self의 수명이 연장된다. 예를 들어, 뷰 컨트롤러에서 네트워크 요청을 하고 이를 중간에 dismiss했을 경우, 해당 클로저가 호출된다. 뷰 컨트롤러를 약하게 캡쳐 할 경우 nil이 된다. class ViewController: UIViewController {
var name: String = "뷰컨"
func doSomething() {
DispatchQueue.global().async {
sleep(3)
print("글로벌큐에서 출력하기: \(self.name)")
DispatchQueue.main.async {
print("메인큐에서 출력하기: \(self.name)")
}
}
}
deinit {
print("\(name) 메모리 해제")
}
}
func localScopeFunction() {
let vc = ViewController()
vc.doSomething()
}
//글로벌큐에서 출력하기: 뷰컨
//메인큐에서 출력하기: 뷰컨
//뷰컨 메모리 해제
class ViewController: UIViewController {
var name: String = "뷰컨"
func doSomething() {
DispatchQueue.global().async { [weak self] in
sleep(3)
guard let self = self else { return }
print("글로벌큐에서 출력하기: \(self.name)")
DispatchQueue.main.async {
print("메인큐에서 출력하기: \(self.name)")
}
}
}
deinit {
print("\(name) 메모리 해제")
}
}
func localScopeFunction() {
let vc = ViewController()
vc.doSomething()
}
//뷰컨 메모리 해제
백그라운드 큐로 전환된 내부에서 UI 업데이트가 메인 큐로 디스패치 된다. async 타입의 호출을 중첩하는 것이 일반적이다.
private func downloadWithGlobalQueue(at indexPath: IndexPath) {
DispatchQueue.global(qos: .utility).async { [weak self] in
guard let self = self else {
return
}
let url = self.urls[indexPath.row]
guard let data = try? Data(contentsOf: url),
let image = UIImage(data: data) else {
return
}
DispatchQueue.main.async {
if let cell = self.collectionView.cellForItem(at: indexPath) as? PhotoCell {
cell.display(image: image)
}
}
}
}
extension CollectionViewController {
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.urls.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "normal", for: indexPath) as! PhotoCell
cell.display(image: nil)
downloadWithGlobalQueue(at: indexPath)
return cell
}
}
private func downloadWithURLSession(at indexPath: IndexPath) {
URLSession.shared.dataTask(with: urls[indexPath.item]) { [weak self] data, response, error in
guard let self = self,
let data = data,
let image = UIImage(data: data) else {
return
}
DispatchQueue.main.async {
if let cell = self.collectionView.cellForItem(at: indexPath) as? PhtoCell {
cell.display(image: image)
}
}
}.resume()
}
let queue = DispatchQueue(label: "xyz")
let workItem1 = DispatchWorkItem {
print("The block of code ran!")
}
let workItem2 = DispatchWorkItem {
print("The block of code ran!")
}
let workItem3 = DispatchWorkItem {
print("The block of code ran!")
}
workItem2.cancel()
workItem1.notify(queue: DispatchQueue.global(), execute: workItem3)
queue.async(execute: workItem1)
| 큐의 종류 | 생성 코드 | 특징 | 직렬/동시 |
|---|---|---|---|
| .main | DispatchQueue.main | 메인 큐 = 메인 쓰레드(UI 업데이트) | Serial |
| .global() | DispatchQueue.global(qos: ) | 6가지 Qos | Concurrent |
| custom | DispatchQueue(label: " ") | QoS추론 / 설정 가능 | default : Serial |
제가 학습한 내용을 요약하여 정리한 것입니다. 내용에 오류가 있을 수 있으며, 어떠한 피드백도 감사히 받겠습니다.
감사합니다.