-
GCD & OperationSwift 2021. 4. 9. 15:02
안녕하세요. 그린입니다🟢
이번 포스팅에서는 일전에 다룬바 있는 동시성 프로그래밍에서 GCD & Operation에 학습해보겠습니다.
이전보다 전체적인 개념에 대한 학습은 같지만 조금 더 심화된 내용이라고 보면 될것 같습니다☺️
우선 동시성 프로그래밍에서 중요한 비동기에 대해 조금 짚고 넘어가보겠습니다.
동시성 프로그래밍을 할때 가장 중요한 부분이 각 스레드별로 비동기적으로 일을 처리하게 해주는 부분입니다.
그래서 각 일을 기다렸다 하지 않고 동시에 처리하도록 만들어줍니다.
왜 작업을 비동기로 처리해야될까?
: 비동기로 처리를 해주는 경우는 굉장히 많습니다. 예를들어 네트워크 작업들은 디폴트가 비동기입니다.
흔히 보는 테이블 및 컬렉션뷰에서 이미지를 서버에서 받아 뿌려주는 인스타그램들의 경우도 만약 비동기가 아닐 시
각각의 이미지가 순서대로 일을 처리하게되어 굉장히 버벅거리고 부자연스럽게 보일겁니다.
: 우리의 애증의 아이폰의 UI는 1/60초마다 화면을 새로 그려주는 리프레싱 작업을 해줍니다.
(1/60초가 사람이 편안하게 자연스럽게 느낄 수 있는 초라고 합니다☺️)
-> 해당 UI에 대한 작업은 전부 메인 스레드에서 진행합니다.
-> 그래서 만약 비동기 처리를 하지 않는다면 해당 리프레싱 작업이 부하가 걸리겠죠?
작업을 여러 스레드에서 동시에 처리하는 방법
: 간단하게 작업을 큐(대기줄)로 보내면 똑똑한 iOS에서 알맞게 여러 스레드로 분배하고 처리해줍니다. (동시처리)
-> 그러면 우리는 동시처리를 위해 단순히 작업을 큐에 넣어주기만 하면 되는겁니다👍🏻
큐를 이용한 작업 분산 처리 방식
: DispatchQueue(GCD)와 OperationQueue 2가지 방식이 있습니다.
분산처리방식의 특징
1) 직접 스레드를 개발자가 건드리지 않아도 큐를 이용해 작업을 분산처리
2) 위 두 방식을 이용해 시스템에서 스레드 수를 알아서 관리
3) 서로 다른 스레드들에서의 작업이 비동기적으로 처리되게함
DispatchQueue(GCD)
: OperationQueue보다 단순한 작업의 분산처리를 해줄때 주로 사용 (서버 통신과 같은 경우)
OperationQueue
: GCD 기반이지만 클래스로 객체화 시켜 더 다양한 기능을 하도록 만들어졌음 (취소/순서/일시중지/상태확인 등..)
"그렇다면 한번 정리해보자면 UI의 최적화를 위하여 메인 스레드 외 다른 여러 스레드에서 작업을 동시 처리를 하는것이 동시성 프로그래밍의 핵심인것 같습니다."
Sync VS Async
-. sync는 함수 호출을 통해 리턴을 받아 완료를 파악하며 상태는 Blocking
-. async는 콜백 함수를 통해 완료를 파악하며 상태는 Non-Blocking
SerialQueue VS CocurrentQueue
-. serialQueue는 작업을 단 하나의 스레드에서 처리하여 순서에 따른 작업을 처리할때 유용
-. cocurrentQueue는 작업을 여러 또다른 스레드에서 처리하여 작업별 다르게 처리할때 유용
GCD 종류
1) main
: 말 그대로 메인 스레드로 시리얼하게 동작
DispatchQueue.main
2) global
: 6종의 QoS(QualityOfServices)와 여러 설정들을 통해 다양하게 동작하며 디폴트 설정은 concurrent
DispatchQueue.global().async{ }
- QoS 종류 및 우선순위
1) userInteractive: UI와 관련된 즉시 처리해야할 부분
2) userInitiated: 유저와 관련있는 작업
3) default: 디폴트 작업
4) utility: 인디케이터가 돌때 할 작업 (네트워킹 등..)
5) background: 백그라운드에서 할만큼 중요하지 않은 작업 (업로드 다운로드 등..)
6) unspecified
3) custom
: 디폴트는 serial이나 라벨을 붙여 커스텀하게 만들어줄 수 있다
let queue = DispatchQueue(label: "green.serial") queue.async{ }
GCD의 CompletionHandler
: 비동기 함수는 리턴으로 설계가 되면 안되고 꼭 콜백 함수를 통해 처리해야한다. (@escaping() -> Void)
-> 말 그대로 비동기이기에 리턴으로 받는다면 그 함수가 끝나는 시점을 계속 기다리게 되는것이다. (모순)
Dispatch Group
: 디스패치 그룹을 통해 그룹 내 요소들의 끝나는 시점을 파악 할 수 있다.
let group = DispatchGroup() DispatchQueue.global().async(group: group){ }
: enter(), leave() 메서드를 통해 비동기 속 비동기 처리 시 출/퇴장 갯수를 카운터해 끝나는 시점을 파악할 수 있다.
OperationQueue 종류
: 오퍼레이션큐는 디스패치큐처럼 종류가 있지 않지만 다양하게 설정할 수 있다.
: 디폴트로는 background QoS를 가지며 concurrent하다.
OperationQueue 기능
1) 시작 :start()
2) 취소: cancelAllOperations()
-> 취소는 시작되지 않고 대기중일때 가능
3) 순서: dependency
OperationQueue에 꼭 필요한 부분
1) input: 큐에 들어올 것
2) output: 작업을 처리하여 보내줄 것
3) main(): 작업에 대한 처리 메서드
"이렇게 구현도 어렵고 디스패치큐로 다 되긴할텐데 오퍼레이션큐가 왜 필요할까라는 질문에 답변을 생각해볼때 오퍼레이션 큐로는 더 세세한 상태(준비/실행/완료)를 관리한다던지 순서를 지정하여 작업을 처리한다던지 조금 더 세밀한 비동기 작업을 할 수 있을것 같다."
UI가 메인 스레드에서 처리되는 이유
: "UIKit의 요소들은 거의 Thread-safe하지 않아 현실적으로 그렇게 할 수 없다"라는것이 함축적인 이유라고 생각하는데 그 안에는 다양한 더 세부적인 이유들이 존재하지만 아직 이해가 가지 않아 이렇게 우선은 결론지었다.
[참고자료]
야곰 iOS 캠프 - Allen 특강 (GCD&Operation)
'Swift' 카테고리의 다른 글
Early Exit (0) 2021.04.13 Optional Chaining (0) 2021.04.12 lazy var (0) 2021.03.17 Swift Performance (0) 2021.03.01 ARC 심화 (0) 2021.02.17