TIL 23

오늘 배운 것

  • GCD
    • 헷갈리는게 많았던 기술이여서 정리한 내용을 간단하게 먼저
      • 큐는 다른 쓰레드로 보내는 작업을 한다.
      • 직렬큐는 다른 쓰레드로 작업을 몰아주고, 동시큐는 여러 쓰레드로 작업을 나눠준다.
      • 클로저 안에 있는 코드들이 하나의 작업이다. 이 하나의 작업이 다 같이 다른 쓰레드로 보내지고, 클로저 안에 있는 코드들은 동기로 실행된다.
      • 비동기는 작업을 보내고 바로 다른일을 한다. 동기는 작업을 보내고 기다린다. 작업을 보낸 자리에 block이 생긴다.
          // 어떤 큐? -> 디스패치 큐
          // 큐의 특성은? -> 메인(직렬)
          // 동기 아니면 비동기? -> 비동기
          DispatchQueue.main.async {
              // 다른 쓰레드에 보내는 작업
          }
        
    • 쓰레드를 하나만 사용할 때 작은 작업은 문제가 없는데, 큰 작업같은 경우는 화면 버벅임 같은 문제가 생길 수 있다. 이를 해결 하기 위해서는 여러개의 쓰레드를 사용해야 하는데 애플이 만든 GCD라는 기술을 사용하면 된다.
    • 어떻게 하면 여러 쓰레드에서 작업을 처리할 수 있을까? Queue에 처리하고 싶은 작업을 보내기만 하면 된다. 그 이후로는 OS가 알아서 쓰레드로 작업을 보낸다. Queue에 들어간 먼저 들어간 작업이 먼저 나가는 방식이다(선입선출). 우리가 직접 쓰레드를 생성할 수 있지만 이와 관련된 지식이 없으면 성능이 오히려 저하가 된다.
    • Queue에는 두 가지가 있는데 첫번째는 DispatchQueue 두번째는 OperationQueue다. 주로 DispatchQueue를 사용한다.
      • 차이점:
        • DispatchQueue: 주로 간단한 작업처리할 때 사용
        • OperationQueue: 복잡한 작업에서 사용. 취소, 순서지정, 일시중지 같은 기능이 있고, DispatchQueue 기반으로 만들어졌다.
    • 동기 vs 비동기
      • 동기: 메인 쓰레드에서 다른 쓰레드로 작업을 보내면, 보낸 작업이 끝날 때까지 기다리는 것. 기다리는 동안 다른일을 하지 않는다. 보낸 작업이 끝나야 다른 작업을 시작한다.
      • 비동기: 비동기는 다른 쓰레드로 보낸 작업을 기다리지 않고 다음 작업을 바로 시작한다.
    • 직렬 vs 동시
      • 직렬과 동시는 큐의 특성에 관한 것이다.
      • 직렬: 큐가 받아들인 작업을 다른 쓰레드로 보내는데 하나의 쓰레드에 작업을 몰아준다.
      • 동시: 큐가 여러개의 쓰레드로 보내는 것을 의미한다. 몇개인지 어떤 쓰레드인지는 OS가 결정한다.
      • 여기서 무조건 동시가 좋아보이고 직렬은 쓸모 없어 보이지만, 직렬은 순서가 중요한 작업을 할 때 필요하다.
    • DispatchQueue의 종류에는 세 가지가 있다. main, global, custom
      • main: 메인쓰레드를 뜻하고 한 개만 존재한다. 그리고 위에서 말한 직렬이다.
      • global: global은 위에서 말한 동시 특성이다. 글로벌큐는 QoS라는 중요도 설정을 가지고 있다. 중요도에 따라 작업을 더 빠르게 처리하고 쓰레드를 더 많이 사용할 수 있다.
      • custom: 디폴트 설정은 직렬이지만 동시성으로 설정이 가능하다. 만드는 방법은 DispatchQueue에 label을 붙여주면 된다. OS가 알아서 QoS를 추론한다.
          DispatchQueue(label: "", attributes: .concurrent)
        
    • GCD 사용시 주의할 점
      • UI는 반드시 메인큐에서만 처리해야 한다.
      • sync 주의 사항
        • 메인 쓰레드에서 네트워크 작업을 다른 쓰레드로 보낼 때, sync로 보내게 되면 작업이 끝날 때 까지 UI가 멈춰버린다
        • async 코드 안에서 같은 큐의 sync를 생성하면 Deadlock이 발생할 수 있다.
            DispatchQueue.global().async {
                // Something
          
                DispatchQueue.global().sync {
          
                }
            }
            // 코드를 보면 global이라는 같은 큐이고, async안에 sync가 있다.
            // 예를 들어서 global().async 클로저 안에 있는 작업을 2번 쓰레드로 보냈다고 하자
            // 작업들이 2번 쓰레드에 있는데 global().sync가 가지고 있는 작업을 동기로 보내게 되면 그 자리에는 block 형성이 돼있는데, 큐로간 작업은 block이 있는 자리로 다시 작업을 보내려고 한다. 이 때 Deadlock이 발생한다.
            // 위의 예시 코드가 항상 Deadlock이 발생하는 것은 아니다. 큐에서는 매번 같은 쓰레드로 작업을 보내는 것이 아니기 때문이다. 하지만 이런식의 코드는 Deadlock이 발생할 수 있다는 것을 명심하자.
          
        • weak, strong 캡쳐 주의
          • 클로저라서 작업을 보낼 때 캡쳐가 발생한다.
          • 예를 들어서 뷰컨트롤러 안에서 큐를 사용할 때 weak self를 써주면 뷰컨트롤러가 dismiss 될 때 큐로 보낸 작업들도 같이 사라진다. 하지만 weak self를 써주지 않으면 강한 참조(strong)이 되기 때문에 뷰컨트롤러가 사라져도 참조가 남아 있어 클로저 안에서는 뷰컨트롤러가 살아있다.