[iOS] RunLoop

Bibi·2022년 1월 26일
0

[iOS] RunLoop

https://babbab2.tistory.com/68

RunLoop란?

: RunLoop 객체는 입력 소스를 처리하는 이벤트 처리 루프.

  • 입력 소스 : 소켓, 파일, 키보드, 마우스 등
    • 예외적으로 Timer의 이벤트 또한 처리한다
  • 쓰레드가 일해야 할 때는 일하고, 일이 없으면 쉬도록 하기 위해 고안

RunLoop 어디에 쓰는가?

: 스레드 의 외부 입력 소스(Input Source) 및 타이머(Timer) 처리

  • 스레드는 모두 각자의 RunLoop를 가질 수 있다 (O)

  • 스레드를 생성할 때 RunLoop가 자동으로 생성된다 (O)

  • 스레드의 RunLoop 는 자동으로 실행된다 (X)

    • RunLoop는 자동 생성되긴 하지만 자동으로 실행되진 않음 - 개발자가 RunLoop 실행을 관리해 주어야 한다.

    • 단, Main RunLoop 는 예외 - 메인 스레드는 애플리케이션이 실행될 때, 프레임워크 차원에서 자동으로 RunLoop를 설정하고 실행한다. 이를 Main RunLoop라고 한다.

따라서 내가 생성한 스레드가 '외부 입력 소스'나 'Timer' 를 처리해야 한다면, RunLoop를 얻어서 실행시켜 주어야 한다.

  • 현재 실행 중인 스레드에서 let runLoop = RunLoop.current 와 같이 현재 스레드의 RunLoop를 얻는다.
  • 얻은 RunLoop를 run() 을 통해 실행시켜 준다.

언제 RunLoop를 사용할까?

RunLoop를 직접 사용하는 경우는 스레드를 직접 만들어 다음과 같은 작업을 할 때이다.

  • 입력 소스를 통해 다른 스레드와 통신하는 경우
  • 타이머를 사용해야 하는 경우
  • 주기적인 일을 계속 수행해야 하는 경우
  • Perform Selector Source 를 사용해야 하는 경우 (?)

RunLoop 작동 원리

RunLoop는 루프 수행 시 2가지 종류의 Event Source 를 수신한다 - Input Source, Timer

  • Input Source : 다른 스레드나 애플리케이션으로부터 온 비동기 이벤트를 전달한다
  • Timer : 예약된 시간 또는 반복 간격으로 발생하는 동기 이벤트를 전달한다

RunLoop는 한 번의 루프 동안 스레드에 도착한 이벤트를 받고, 이에 대한 핸들러를 수행하는 객체이다.

✅ RunLoop는 이름이 '루프'이긴 하지만, 스스로 반복 실행을 하지는 않는다. 한 번 Event Source를 읽고 전달하면 이후 '대기'상태가 된다. 그래서 다음 이벤트가 발생해도 RunLoop 가 대기 상태이기 때문에 이벤트를 받을 수 없다. - 따라서 스레드 내에서 개발자가 명시적으로 for, while 등을 통해 RunLoop를 반복 실행시켜주어야 한다.

RunLoop (반복)실행 시키기

RunLoop를 실행을 위해 4가지 메서드가 있다.

*보통은 run(until:) 만 사용한다.

  • func run()

    • refecer를 영구 루프에 넣고, run() 실행 전 선언된 모든 Input Source 의 데이터를 처리
  • func run(mode: RunLoop.Mode, before: Date) -> Bool

    • 루프를 한 번 실행해 지정 모드에서 지정 날짜까지 input 을 blocking 한다
  • func run(until: Date)

    • 지정 날짜까지 루프를 실행하고, 그 기간 동안 루프는 부착된 모든 Input Source 들의 데이터를 처리한다.

    • 보통 RunLoop를 반복 실행할 때 이 메서드를 사용 - RunLoop를 실행시키고, 원하는 만큼 반복

    • 예시 코드

    • DispatchQueue.main.async {
          var isRunning = true
          let runLoop = RunLoop.current
      
          Timer.scheduledTimer(withTimeInterval: 3, repeats: true) {_ in
            print("Beep!")                                                           
          }
      
          while isRunning {
             runLoop.run(until: Date().addingTimeInterval(1))
          }
      }
  • func acceptInput(forMode: RunLoop.Mode, before: Date)

    • 지정된 모드에 대한 인풋만을 받아 루프를 한 번 또는 특정 날짜까지 실행한다.

0개의 댓글