Day 39 - (GCD, performSelector())

sun02·2021년 9월 29일
0

100 days of Swift 

목록 보기
31/40

if let url = URL(string: urlString) {
    if let data = try? Data(contentsOf: url) {
                parse(json: data)
                return
    }
}
  1. Blocking call : Data의 contentsOf를 사용하여 인터넷에서 데이터를 다운로드하는 것
  • 서버에 연결되고 모든 데이터를 완전히 다운로드할 때까지 메서드의 추가 실행 코드를 차단한다.
  1. 백그라운드에서 앱이 여러 명령을 실행하므로 여러 CPU 코어를 사용할 수 있다. 각각의 CPU는 서로 독립적으로 작업을 수행할 수 있으므로 성능이 크게 향상된다. 이러한 코드 실행 프로세스를 스레드라고 한다.
  • 스레드는 제공한 코드를 실행하며 무작위로 실행하지않는다. 즉, 다른 CPU에서 작업할 스레드를 생성하지 않았다면 기본적으로 코드는 하나의 CPU에서만 실행된다.
  • 모든 UI 작업은 메인스레드에서 이루어져야한다. 다른 스레드에서 UI코드를 실행하려고 하면 작동할 수도, 작동하지 않을 수도, 충돌하거나 예기치 않은 결과를 일으킬 수도 있다.
    • 메인스레드 : 프로그램이 생성된 초기 스레드
  • 스레드가 실행되는 시기, 순서를 조절할 수 없다. 스레드를 생성하고 시스템이 스레드를 실행할 수 있게 하면 시스템은 최선을 다해 실행한다.
  • 실행 순서를 조절하지 않기 때문에 너는 한 번에 하나의 스레드만 데이터를 수정하도록 코드에 각별한 주의가 필요하다

1, 2의 이유로 위 코드가 잘못되었다는 것을 알 수 있다. 모든 UI코드는 메인스레드에서 실행되어야하고 Data contentof로 메인스레드를 막게 되므로 전체 프로그램이 정지되고 사용자는 화면에서 아무것도 할 수 없게된다.

데이터가 다운로드되면 프로그램 정지가 해제되지만 이것은 끔찍한 경험이다..

일반적으로 원격 리소스에 접근하는 경우 백그라운드 스레드에서 수행해야한다.

  • 백그라운드 스레드 : 기본 스레드를 제외한 모든 스레드

GCD

GCD는 멀티스레딩으로 알려진 여러 스레드를 만들고 작업하는 번거로움을 줄여준다.
스레드 생성, 소멸이나 현재 장치에 대해 최적의 스레드 수를 생성했는지 확인할 필요가 없다.
GCD는 자동으로 스레드를 생성하고 가장 효율적인 방법으로 스레드에서 코드를 실행한다.

- async()

: 가장 중요한 GCD함수로 "다음 코드를 비동기식으로 실행" 을 의미한다.

일부 코드를 백그라운드 스레드로 푸시하거나, 메인 스레드로 푸시하여 메인스레드에서 안전하게 ui를 업데이트할 수 있다.

async()를 호출하는 방법은 코드를 실행할 위치를 시스템에 알리는 것이다.
GCD는 queue와 매우 유사하게 작동한다.
GCD 호출은 실행할 스레드를 생성하는 것이 아니라 기존의 스레드 중 하나에 할당되는 것이다.
GCD는 여러 queue를 생성하고 중요도에 따라 작업을 배치한다.
모두 FIFO이기 때문에 코드 블록은 삽입된 순서대로 queue에서 제거되지만 둘 이상의 코드 블럭이 동시에 실행될 수 있기 때문에 완료 순서는 보장되지 않는다.

- QoS

일부 코드의 중요도는 QoS(quality of service)에 따라 달라진다.
QoS위에는 메인스레드에서 실행되는 main queue가 있고 그 뒤에 사용할 네 개의 background queue가 있으며 각각은 QoS 레벨이 설정되어있다.

  • User Interactive : 가장 우선순위가 높은 백그라운드 스레드. UI작업을 유지하는데 중요한 작업을 수행하도록 할 때 사용한다.
  • User Initiated : 앱을 지속적으로 사용하기 위해 현재 대기 중인 요청된 작업을 실행하는데 사용
  • The Utility Queue : 사용자가 알고 있기만 당장 필수적이지 않은 장기 작업에 사용
  • The Background Queue : 사용자가 적극적으로 인식하지 못하거나 진행상황이나 완료 시점에 신경 쓰지않는 장기 작업에 사용

- main -> background


DispatchQueue.global().async{

async()를 사용하여 모든 로딩 코드가 백그라운드 큐에서 실행되도록 함


DispatchQueue.global(qos: .userInitiated).async() {

default대신 적절한 qos를 지정하고 싶다면 다음과 같이 작성한다.

async() 메서드는 하나의 매개변수를 가지는데 비동기적으로 실행되는 클로져이다. 후행 클로져 구문을 사용한다. 강한 참조 주기가 없도록 하기 위해서 [weak self]로 시작한다고 생각할 수 있지만 GCD는 코드를 한 번 실행한 다음 버리기 때문에 그렇지 않다.

DispatchQueue.global(qos: .userInitiated).async() {
	
	if let url = URL(string: urlString) {
    	if let data = try? Data(contentsOf: url) {
                parse(json: data)
                return
        }
    }
}

- background -> main

사용자 인터페이스 작업을 백그라운드 스레드에서 수행하는것은 절대 좋지 않다.
만약 지금 백그라운드 스레드에 있고 메인스레드에서 코드를 실행하고 싶다면 async()를 다시 호출하면된다.


Dispatch.main.async() {
	self.tableView.reloadData()
}

- performSelector()

: GCD를 사용하는 또다른 방법으로 performSelector(inBackground:)와 performSelector(onMainThread:)가 있다.

둘 다 같은 방식으로 작동된다. inBackground는 백그라운드 스레드에서 실행하고 onMainThread는 메인 스레드에서 실행한다.


tableView.performSelector(onMainThread: #selector(UITableView.reloadData), with: nil, waitUntilDone: false)

0개의 댓글