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 |
제가 학습한 내용을 요약하여 정리한 것입니다. 내용에 오류가 있을 수 있으며, 어떠한 피드백도 감사히 받겠습니다.
감사합니다.