본문 바로가기
Apple/iOS

[iOS] Timer(타이머)와 Thread(스레드), RunLoop(런루프)

by 어멘드 2022. 1. 10.
반응형

** 아직 공부하는 중이라 틀린 내용이 있을 수도 있습니다. **

 최근 프로젝트에서 반복 타이머가 필요한 경우가 있었는데, 그때 알아보았던 타이머, 스레드, 런루프에 대해 까먹기 전에 정리하려고 한다. 


Timer

 찾아본 바로는 애플이 제공하는 반복 타이머는 두 종류가 있는데, Timer와 DispatchSourceTimer이다. Timer는 굉장히 오래됐고, DispatchSourceTimer는 비교적 최근에 나왔다고 한다. 이 중 내가 사용했던 것은 Timer 클래스이다.

 애플 공식 문서에서는 Overview부터 "타이머는 런루프와 함께 작동한다. 타이머를 효과적으로 사용하려면 런 루프가 어떻게 작동하는지 알아야 한다. 스레딩 프로그래밍 가이드를 참조해라."라고 나와있다.

 그래서 링크 걸려있는 "Threading Programming Guide"로 런루프부터 공부하러 갔다...


RunLoop

 일단 Run Loop를 한 줄로 요약하자면 "이벤트 처리 루프"이다. 스레드에 들어온 이벤트를 받아서 처리하는 루프인 것이다.

 이벤트는 두 종류로 나뉜다. 하나는 입력 소스, 그리고 하나는 타이머 소스이다.

 타이머 소스는 말 그대로 우리가 사용하려고 하는 그 타이머일 것이고, 입력 소스는 "다른 스레드나 다른 애플리케이션에서 들어오는 비동기 이벤트로 포트 기반 소스와 사용자 정의 소스로 나눠진다"라고 하는데 뭔지 감이 안 잡혀서 공식문서의 Run Loop 페이지로 이동했다. (방금 전까지의 내용은 스레드 프로그래밍 가이드에 있던 것)

 여기가 훨씬 쉬운 문장으로 되어 있었다. 입력 소스는 마우스나 키보드 이벤트와 같은 것들을 말하는 것이었다.

 또 마지막에 "타이머는 입력이 아닌 특별한 유형입니다."라고 적혀있다. 타이머는 마우스/키보드 같은 입력 소스와는 구별되는 타입이라는 것이다.

 요약하면, 런 루프는 마우스나 키보드 같은 입력 이벤트와 타이머 이벤트를 처리해주는 루프이다. 결론은, 타이머 작동을 위해서는 Run Loop가 필요하다!

 Run Loop는 또 스레드와 밀접한 관련이 있다. 메인 스레드를 포함한 각 스레드에는 Run Loop가 존재하는데, 이 Run Loop의 목적은 스레드가 일할 땐 일하고, 쉴 땐 쉬게 하는 것이다.

일단 여기까지 보고 다시 Timer 문서로 돌아가 보자.

반응형

Timer

 "Run loops maintain strong references to their timers, so you don't have to maintain your own strong reference to a timer after you have added it to a run loop"라는 문장이 있다.

 Timer를 작동시키기 위해 Run Loop에 타이머를 추가하고 나면, Run Loop가 Timer에 대한 강한 참조를 유지하기 때문에 따로 강한 참조를 두지 않아도 Timer 인스턴스가 사라지지 않는다.

 

 이제 타이머를 만드는 방법이다. 타이머는 한 번에 하나의 Run Loop에만 등록할 수 있다. 타이머를 만드는 방법은 여러 가지가 있는데, 크게 두 가지로 나눌 수 있다.

 Timer 객체를 만들어서 현재 Run Loop에 추가까지 하는 scheduledTimer()

 Timer 객체를 만들기만 하는 init()이다.

 init()의 경우 Timer 객체를 만들기만 했기 때문에 사용하려면 add(_:forMode:) 메소드로 Run Loop에 수동으로 추가해주어야 한다. Run Loop Mode에 대해서는 다음 글에서..!

let timerA = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: {_ in print("A")})
let timerB = Timer.init(timeInterval: 0.5, repeats: true, block: {_ in print("B")})
RunLoop.current.add(timerB, forMode: .default)	// add하지 않으면 B는 출력 안됨

// 참조를 유지하지 않아도 C가 계속 출력됨
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: {_ in print("C")})

 


요약

  1. Timer 이벤트 처리는 Run Loop가 담당한다. 따라서 Timer 작동을 위해서는 Run Loop에 Timer를 추가해야 한다.
  2. Run Loop는 Timer에 대한 강한 참조를 유지하므로, 따로 강한 참조를 유지하지 않아도 된다.
  3. 타이머를 만드는 방법은 크게 두 가지가 있다. Timer 객체를 만든 뒤 Run Loop에 자동으로 추가해주는 scheduledTimer() 메소드와, 객체를 만들기만 해주는 init()이 있다.

 

반응형

댓글