본문 바로가기
IOS - Swift

[iOS/Swift] Run Loop ( 2 / 2 )

by 게게겍 2023. 8. 27.

Run Loop 작동원리

Run Loop는 루프를 수행할 때 2가지 Event Source를 수신합니다.
     

1. Input Source

다른 thread 나 App으로부터 온 비동기 이벤트를 전달합니다.
   

 2. Timer

예약된 시간이나 간격마다 동기 이벤트를 전달한다.

위 그림에서 Thread에서 노란색 Run loop를 한 바퀴 도는 작업이 한 번의 실행이라고 가정한다면,

루프는 한 바퀴를 돌면서 Event들을 모아놨다가, 한꺼번에 처리하는 방식으로 작동하게 됩니다.

이 Run loop는 한 번 event source를 읽고 전달하는 과정이 끝나면 그대로 대기합니다.

즉, 내부적으로 반복 실행이 되지 않기 때문에

스레드 내에서 프로그래머가 while, for문을 통해 명시적으로 반복 실행시켜줘야 한다.

RunLoop 실행방법

앞전에 스레드는 각각의 RunLoop를 가지며 스레드가 생성될 때 RunLoop가 자동으로 생성된다. 하지만 자동으로 실행은 하는건 아니다.

라고 말햇습니다. 링크

하지만 예외적으로, Main Thread는 application 이 실행될 때 프레임워크 차원에서 RunLoop를 자동으로 설정하고 실행하는데 이를 main RunLoop 라고 합니다.

런루프를 직접 실행하기 위해서는, 런루프 객체에 접근을 해야하며

이를 실행하기 위해서는 RunLoop의 프로퍼티인 Current를 사용합니다.

 

공식 문서를 보면

 

current의 반환 값은 현재 스레드에 대한 NSRunLoop 개체 라고 작성되어 있습니다.

또 스레드에 대한 런 루프가 아직 존재하지 않는다면 하나가 생성되어 반환된다고 하는데, 현재 스레드에 대한 런 루프를 참조하는 방식은 아래와 같습니다.

let runLoop = RunLoop.current

 

 

런 루프를 실행시키기 위한 메서드로는 총 네 가지가 있습니다.

  • run(): 실행할 당시, 루프에 들어와 있는 Input source와 Timer source를 영구적으로 처리한다.

 

DispatchQueue.global().async { //비동기로 글로벌 디스패치 병렬실행
    let runLoop = RunLoop.current // 이벤트 처리

    Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
        print("Running run loop")
    } //0.5초 간격으로 true 반복출력
    runLoop.run()
}

주의: 영구적으로 처리되기 때문에 실행 이후에 들어오는 이벤트는 이전 이벤트가 반환되기 전까지 처리되지 않으므로 주의해서 사용해야합니다.

DispatchQueue.global().async {
    let runLoop = RunLoop.current

    Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { _ in
        print("Running timer1")
    }
    runLoop.run()

    Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { _ in
        print("Running timer2") // 앞에꺼가 돌아가고 있어서 실행될수 없음
    }
}
  • run(until:): 가장 대표적으로 사용되는 메서드이다. 지정된 date까지 루프를 실행하고, 그 시간 동안 연결된 모든 input 데이터를 처리한다.

이전에 부착된 이벤트만 영구적으로 실행되는 run()과 달리,

이 메서드는 루프의 실행 시간을 지정해줄 수 있습니다.

보통 while, for문 등의 반복문을 써서 연속적으로 실행시킬 수 있습니다.

RunLoop Mode

  • run(mode:before:): 루프를 한 번 실행하고, 특정 모드에서 주어진 date까지 input을 blocking 한다.
  • acceptInput(forMode:before:): 루프를 한 번 또는 주어진 date까지 실행하고, 특정 모드에 대한 input만 수락한다.

언제 RunLoop를 사용해야 하는가


런 루프를 직접 사용하는 경우는 다음과 같은 작업을 할 때이다.

  • Input source를 통해 다른 스레드와 통신하는 경우
  • 타이머를 사용하는 경우
  • performSelector를 사용하는 경우
  • 주기적인 작업을 계속 수행해야 하는 경우

참고

https://developer.apple.com/documentation/foundation/runloop

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW5

https://babbab2.tistory.com/68

https://withthemilkyway.tistory.com/58?category=1054698#Run%--Loop%--�%-B%A-�%--%--%--�%B-%A-�%B-%--