상세 컨텐츠

본문 제목

Timer 사용해보기

iOS

by 쑤야. 2023. 12. 24. 20:33

본문

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

 

Docs


 

 

Overview


  • 타이머는 실행 루프와 함께 작동한다. 실행 루프는 타이머에 대한 강력한 참조를 유지하므로 타이머를 실행 루프에 추가한 후 타이머에 대한 강력한 참조를 유지할 필요가 없다.
  • 타이머는 실시간 메커니즘이 아니다. 따라서 long run loop가 호출되거나, run loop가 타이머를 모니터링하지 않는 모드에서 타이머의 실행이 발생하게 될 경우, 다음 run loop 확인 전까지 타이머가 실행되지 않아 실제 시간과 오차가 발생할 수도 있다.

 

Comparing Repeating and Nonrepeating Timers


  • Timer 생성 시 반복인지, 비반복인지를 지정해줘야 한다.
  • 비반복 타이머는 한 번 실행된 후, 자동으로 자신을 무효화한다.
  • 반복 타이머는 run loop에서 실행되고 나서 다시 일정을 조정하며, 실제 실행 시간과는 달리 항상 예약된 실행 시간을 기준으로 자신을 예약한다.
    • 만약 interval이 5초라고 할 때, 5.3초에 발사가 되더라도 이후 발사가 10.3에 예약되는 것이 아니라 10에 예약되는 것이다.

 

Timer Tolerance


  • 오차 범위(tolerance)를 지정해 유연성을 향상시킬 수 있다.
  • 타이머가 작동할 때 이러한 유연성은 시스템의 절전 및 응답성 향상을 위한 최적화 기능을 향상시킨다.
  • 일반적인 오차 범위 지정으로는 interval 값의 10% 이상으로 설정하며, 기본값은 0이다.

 

Scheduling Timers in Run Loops


방법 1. 

  • 현재 run loop에 default 모드로 스케줄
scheduledTimer(timeInterval:invocation:repeats:)
scheduledTimer(timeInterval:target:selector:userInfo:repeats:)

방법 2.

  • run loop에 스케줄링 없이 Timer 인스턴스 생성
  • 인스턴스 생성 이후, 적절한 run loop에 add(_:forMode:) 메서드를 통해 타이머를 추가해줘야 한다.
init(timeInterval:invocation:repeats:)
init(timeInterval:target:selector:userInfo:repeats:)

 


방법 3.

  • run loop에 스케줄링 없이 Timer 인스턴스 생성
  • 인스턴스 생성 이후, 적절한 run loop에 add(_:forMode:) 메서드를 통해 타이머를 추가해줘야 한다.
  • 방법 2와 달리 타이머가 종료되는 시간을 지정해줘야 한다.
init(fireAt:interval:target:selector:userInfo:repeats:)

 

 

활용


1. 현재 스레드에서의 실행

  • 아래 조건에 부합한다면(?), 방법 1이 가장 쉽게 사용할 수 있는 방법이다.

    👉🏻 ViewController와 같은 Context에서 사용하길 원하며, 다른 스레드에서 동작하도록 설정하지 않는 경우

 

timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateTimer), userInfo: nil, repeats: true)
timer.tolerance = 0.1

2. 다른 스레드에서의 실행

  • 아래 2가지 방식은 모두 동일한 동작을 수행한다. 단지 Timer를 생성하고, 등록하는 방법이 다를 뿐.
  • Main Thread에서 Timer를 동작시킬 경우, Serial 하게 동작하기 때문에 동작 지연을 방지하고자 background에서 실행되도록 코드를 작성했다.
  • 두 방식 모두 반드시 run loop의 run() 메서드를 호출해야 한다.

 

2-A. scheduledTimer 활용

  • background run loop에서 scheduledTimer이 선언되었기 때문에 자동으로 backgound의 run loop에 추가된다.
DispatchQueue.global(qos: .background).async {
    self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateTimer), userInfo: nil, repeats: true)
    self.timer?.tolerance = 0.1
    RunLoop.current.run()
}

 

2-B. init 활용

  • background run loop 내부 코드이기 때문에 RunLoop.current는 background 스레드를 지칭한다.
DispatchQueue.global(qos: .background).async {
    let timer = Timer(timeInterval: 1, target: self, selector: #selector(self.updateTimer), userInfo: nil, repeats: true)
    timer.tolerance = 0.1
    RunLoop.current.add(timer, forMode: .default)
    RunLoop.current.run()
}

 

 

 

 

관련글 더보기