📚 GCD
iOS에서 멀티코어 프로세서에 코드를 동시에 실행시키게 해주는 프레임 워크
즉 멀티 스레드 환경에서 다수의 스레드에 작업을 적절히 분배시키는 방법이다.
Swift를 이용하여 개발을 하다보면 한번쯤은 만난 Dispatch Queue가 있다. Dispatch Queue가 바로 GCD에서 사용하는 Queue다..!
그럼 GCD가 하는 것은 무언인가?? -> 프로그래머가 Dispatch Queue에 작업을 보내면 스레드를 적절히~~ 생성하고 실행하며 작업이 종료되면 스레드를 제거한다.
- DisptchQueue : iOS에서 동시성 프로그래밍을 돕기 위해 제공하는 Queue
📙 비동기와 동기(async/sync)
동기와 비동기를 나눌 때 가장 중요한점은 메인스레드가 기다리냐 안기다리냐!!
비동기(async)
차가운 메인 스레드는 기다려 주지 않는다.
DispatchQueue.global().async {
task
}
- global dispatch queue에 작업을 비동기적으로 보낸다.
- 메인 스레드에서 global dispatch queue에 task를 보낸 후 작업의 종료 유무와 상관없이 바로 다음 작업을 시작한다.
- task가 끝나는 시점은 swift에서 클로저를 통해 알려준다
- completionHandler / completion
동기(sync)
따듯한 메인 스레드는 작업이 끝나기를 기다려준다
DispatchQueue.global().sync {
task
}
- 메인 스레드가 task의 종료를 기다린 후에 다음 작업을 수행한다.
! 비동기가 그럼 왜 필요할까 !
- 시간단축
- 네트워크 관련 작업들은 내부적으로 비동기로 구현되어있다. ex) URLSession
📙 Serial/Concurrent
두가지 모두 GCD에서 사용하는 Queue의 특성을 나타낸다
Serial
메인 스레드에서 분산 처리 시킨 작업을 다른 한개의 스레드에서 처리하도록 한다

- GCD는 serial Queue에 담긴 작업들을 단 한개의 스레드에 분배해준다.(이러면 분배가 아니지 맞나..?)
- 모든 작업이 이전 작업의 종료를 기다린다.
- 각 task의 시작과 종료 순서에 대한 예측이 용이하다
- 실행 순서가 중요한 작업들을 처리하고 할 때 사용하면 좋다!
Concurrent
메인 스레드에서 분산 처리 시킨 작업을 여러개의 스레드에서 처리하도록 한다

- GCD는 concurrent queue에 담긴 작업을 여러 스레드에 분배한다.
- 각 작업이 끝나는 순서를 알 수 없다.(여러 스레드여서)
- 작업들이 끝나는 순서보다 빠른 수행이 중요할 때 사용한다.
여기서 동기/비동기 + serial/concurrent를 조합하면 4개의 Queue를 만들 수 있다.
📙 Dispatch Queue의 종류
우리는 보통 DispatchQueue.global() 을 많이 사용한다. 하지만 실제로 iOS에서 제공하는 큐의 종류는 3가지다. 각 Queue의 종류에 따라 Queue의 특성이 다르기 때문에 작업에 따라 적절한 Queue를 선택하는 것은 중요하다.
Main Queue
- 오직 한개만 존재하며 serial한 특성을 가지고 있다.
- 즉 메인 스레드에서 모든 Task를 처리한다.
- UI를 엡데이트 할 때 많이 사용한다 -> 그래서 Drive Signal을 사용(Main을 보장해줘서)
DeadLock 예시
DispatchQueue.main.async {
DispatchQueue.main.sync {
task
}
}
- A queue에 작업 할당.
- B queue에 작업 할당. (A와 B는 동일한 main serial queue).
- B는 Sync이기 때문에 자신의 작업을 우선적으로 실행기 위해 queue를 block함
- 기존의 queue에 있는 작업이 끝나야 B가 실행되는데 이미 block되어 버림
- 따라서 DeadLock 발성
Global Queue
- Concurrent 특성을 가짐
- QoS(Quality of Service)에 따라 알맞은 스레드에 나눠진다.
Custom Queue
- 프로그래머(내가) 만드는 큐
- 디폴트는 Serial특성을 가지며 QoS 설정이 가능하다
📙 QoS(Quality of Service)
작업의 중요도와 우선순위를 지정하기 위해 사용하는 변수
Qos는 총 3가지를 가진다
userInteractive
- 사용자와 직접 상호작용하는 작업 ex) UI업데이트, 애니며이션 등
- 메인 스레드에서 직렬로 처리하면 시간이 오래 걸릴 수 있는 작업을 userInteractive에서 처리해 바로 동작하는 것처럼 보임
userInitiated
- 사용자가 시작한 작업에 사용 ex) 파일 열기, 데이터 가져오기 등
- 빠른 응답, 즉각적인 결과가 필요한 작업
- userInteractive보다 느릴수 있으며 유저가 이를 인지함
default
- 기본 QoS 레벨, 명시적으로 설정하지 않은 경우 사용
- 일반적인 작업네 적합하다.
utility
- 오래 걸리지만 사용자에게 중요한 작업 ex) 네트워크 요청, 파일 다운로드 등
- 보통 progress bar와 함께 길게 진행되눈 작업니다.
background
- 사용자가 직접 인식하지 못하는 작업
- DB정리, 백업 작업 등
unspecified
우선순위가 높은 Queue에 더 많은 스레드를 배치한다!!
📙 주의사항
- UI업데이트는 반드시 메인 스레드에서 처리!(밑줄 2개..!)
- 메인스레드가 UI그리는 일을 담당한다 -> iOS에서만 해당되는 것이 아님
이번 프로젝트 진행하며 초반에 가장 많이 지적받은 사항..
- 메인 큐에서 다른 큐로 작업을 보낼 때 sync를 사용해서는 안된다.
- sync로 보내게 되면 다른 작업을 기다린다는 의미
- 메인 스레드는 UI를 업데이트 해야해서 기다릴 수 없다
- 현재 작업중인 큐와 동일한 큐에 sync로 작업을 보내선 안된다.
- 각 큐에서 사용하는 스레드는 정해져 있다.
- 같은 큐에 작업을 보내면 동일한 스레드에 배치될 수 있다.
- 동일한 스레드에 보내진 작업이 끝날 때 까지 보낸 작업도 수행될 수 없는 DeadLock 발생
- 객체애 대한 캡처 주의
- taks를 큐에 보낸다 = 클로저를 보낸다.
- 객체에 대한 캡처 현상 발생할 수 있다 -> 순환 참조 발생 가능
- weak self 사용으로 방지
순환 참조 예시
