axiom-timer-patterns-ref

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Timer Patterns Reference

定时器模式参考

Complete API reference for iOS timer mechanisms. For decision trees and crash prevention, see
axiom-timer-patterns
.

iOS定时器机制的完整API参考。如需决策树和崩溃预防相关内容,请参考
axiom-timer-patterns

Part 1: Timer API

第一部分:Timer API

Timer.scheduledTimer (Block-Based)

Timer.scheduledTimer(基于闭包)

swift
// Most common — block-based, auto-added to current RunLoop
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
    self?.updateProgress()
}
Key detail: Added to
.default
RunLoop mode. Stops during scrolling. See Part 1 RunLoop modes table below.
swift
// 最常用的方式——基于闭包,自动添加到当前RunLoop
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
    self?.updateProgress()
}
关键细节:添加到
.default
RunLoop模式,在滚动时会停止触发。详见下文第一部分的RunLoop模式表格。

Timer.scheduledTimer (Selector-Based)

Timer.scheduledTimer(基于选择器)

swift
// Objective-C style — RETAINS TARGET (leak risk)
let timer = Timer.scheduledTimer(
    timeInterval: 1.0,
    target: self,       // Timer retains self!
    selector: #selector(update),
    userInfo: nil,
    repeats: true
)
Danger: This API retains
target
. If
self
also holds the timer, you have a retain cycle. The block-based API with
[weak self]
is always safer.
swift
// Objective-C风格——会强引用TARGET(存在内存泄漏风险)
let timer = Timer.scheduledTimer(
    timeInterval: 1.0,
    target: self,       // Timer会强引用self!
    selector: #selector(update),
    userInfo: nil,
    repeats: true
)
注意:该API会强引用
target
。如果
self
同时持有timer,会形成循环引用。使用
[weak self]
的闭包API始终更安全。

Timer.init (Manual RunLoop Addition)

Timer.init(手动添加到RunLoop)

swift
// Create timer without adding to RunLoop
let timer = Timer(timeInterval: 1.0, repeats: true) { [weak self] _ in
    self?.updateProgress()
}

// Add to specific RunLoop mode
RunLoop.current.add(timer, forMode: .common)  // Survives scrolling
swift
// 创建定时器但不添加到RunLoop
let timer = Timer(timeInterval: 1.0, repeats: true) { [weak self] _ in
    self?.updateProgress()
}

// 添加到指定RunLoop模式
RunLoop.current.add(timer, forMode: .common)  // 滚动时仍会触发

timer.tolerance

timer.tolerance

swift
timer.tolerance = 0.1  // Allow 100ms flexibility for system coalescing
System batches timers with similar fire dates when tolerance is set. Minimum recommended: 10% of interval. Reduces CPU wakes and energy consumption.
swift
timer.tolerance = 0.1  // 允许100ms的误差,供系统合并定时器任务
设置误差后,系统会将触发时间相近的定时器进行批处理。建议最小误差为时间间隔的10%,可减少CPU唤醒次数,降低能耗。

RunLoop Modes

RunLoop模式

ModeConstantWhen ActiveTimer Fires?
Default
.default
/
RunLoop.Mode.default
Normal user interactionYes
Tracking
.tracking
/
RunLoop.Mode.tracking
Scroll/drag gesture activeOnly if added to
.common
Common
.common
/
RunLoop.Mode.common
Pseudo-mode (default + tracking)Yes (always)
模式常量激活场景定时器是否触发?
默认
.default
/
RunLoop.Mode.default
常规用户交互时
追踪
.tracking
/
RunLoop.Mode.tracking
滚动/拖拽手势激活时仅当添加到
.common
模式时触发
通用
.common
/
RunLoop.Mode.common
伪模式(默认+追踪)始终触发

timer.invalidate()

timer.invalidate()

swift
timer.invalidate()  // Stops timer, removes from RunLoop
// Timer is NOT reusable after invalidate — create a new one
timer = nil          // Release reference
Key detail:
invalidate()
must be called from the same thread that created the timer (usually main thread).
swift
timer.invalidate()  // 停止定时器,从RunLoop中移除
// 调用invalidate()后定时器无法复用——需创建新的定时器
timer = nil          // 释放引用
关键细节
invalidate()
必须在创建定时器的同一线程调用(通常为主线程)。

timer.isValid

timer.isValid

swift
if timer.isValid {
    // Timer is still active
}
Returns
false
after
invalidate()
or after a non-repeating timer fires.
swift
if timer.isValid {
    // 定时器仍处于激活状态
}
调用
invalidate()
后或非重复定时器触发后,返回
false

Timer.publish (Combine)

Timer.publish(Combine)

swift
Timer.publish(every: 1.0, tolerance: 0.1, on: .main, in: .common)
    .autoconnect()
    .sink { [weak self] _ in
        self?.updateProgress()
    }
    .store(in: &cancellables)
See Part 3 for full Combine timer details.

swift
Timer.publish(every: 1.0, tolerance: 0.1, on: .main, in: .common)
    .autoconnect()
    .sink { [weak self] _ in
        self?.updateProgress()
    }
    .store(in: &cancellables)
完整的Combine定时器细节请见第三部分。

Part 2: DispatchSourceTimer API

第二部分:DispatchSourceTimer API

Creation

创建

swift
// Create timer source on a specific queue
let queue = DispatchQueue(label: "com.app.timer")
let timer = DispatchSource.makeTimerSource(flags: [], queue: queue)
flags: Usually empty (
[]
). Use
.strict
for precise timing (disables system coalescing, higher energy cost).
swift
// 在指定队列上创建定时器源
let queue = DispatchQueue(label: "com.app.timer")
let timer = DispatchSource.makeTimerSource(flags: [], queue: queue)
flags:通常为空数组
[]
。使用
.strict
可实现精确计时(禁用系统合并机制,能耗更高)。

Schedule

调度

swift
// Relative deadline (monotonic clock)
timer.schedule(
    deadline: .now() + 1.0,     // First fire
    repeating: .seconds(1),     // Interval
    leeway: .milliseconds(100)  // Tolerance (like Timer.tolerance)
)

// Wall clock deadline (survives device sleep)
timer.schedule(
    wallDeadline: .now() + 1.0,
    repeating: .seconds(1),
    leeway: .milliseconds(100)
)
deadline vs wallDeadline:
deadline
uses monotonic clock (pauses when device sleeps).
wallDeadline
uses wall clock (continues across sleep). Use
deadline
for most cases.
swift
// 相对截止时间(单调时钟)
timer.schedule(
    deadline: .now() + 1.0,     // 首次触发时间
    repeating: .seconds(1),     // 时间间隔
    leeway: .milliseconds(100)  // 误差(类似Timer.tolerance)
)

// 挂钟截止时间(设备休眠时仍会运行)
timer.schedule(
    wallDeadline: .now() + 1.0,
    repeating: .seconds(1),
    leeway: .milliseconds(100)
)
deadline vs wallDeadline
deadline
使用单调时钟(设备休眠时暂停计时)。
wallDeadline
使用挂钟(设备休眠时仍继续计时)。大多数场景下使用
deadline
即可。

Event Handler

事件处理

swift
timer.setEventHandler { [weak self] in
    self?.performWork()
}
Before cancel: Set handler to nil to break retain cycles:
swift
timer.setEventHandler(handler: nil)
timer.cancel()
swift
timer.setEventHandler { [weak self] in
    self?.performWork()
}
取消前:将处理程序设为nil以打破循环引用:
swift
timer.setEventHandler(handler: nil)
timer.cancel()

Lifecycle Methods

生命周期方法

swift
timer.activate()   // Start — can only call ONCE (idle → running)
timer.suspend()    // Pause (running → suspended)
timer.resume()     // Unpause (suspended → running)
timer.cancel()     // Stop permanently (must NOT be suspended)
swift
timer.activate()   // 启动——仅能调用一次(闲置→运行)
timer.suspend()    // 暂停(运行→暂停)
timer.resume()     // 恢复(暂停→运行)
timer.cancel()     // 永久停止(调用时必须处于非暂停状态)

State Machine Lifecycle

状态机生命周期

                    activate()
        idle ──────────────► running
                               │  ▲
                    suspend()  │  │  resume()
                               ▼  │
                            suspended
                    resume() + cancel()
                           cancelled
Critical rules:
  • activate()
    can only be called once (idle → running)
  • cancel()
    requires non-suspended state (resume first if suspended)
  • cancelled
    is terminal — no further operations allowed
  • Dealloc requires non-suspended state (cancel first if needed)
                    activate()
        idle ──────────────► running
                               │  ▲
                    suspend()  │  │  resume()
                               ▼  │
                            suspended
                    resume() + cancel()
                           cancelled
重要规则
  • activate()
    仅能调用一次(闲置→运行)
  • cancel()
    要求定时器处于非暂停状态(若已暂停需先恢复)
  • cancelled
    为终态——无法再进行任何操作
  • 释放定时器前需确保其处于非暂停状态(必要时先取消)

Leeway (Tolerance)

Leeway(误差)

swift
// Leeway values
timer.schedule(deadline: .now(), repeating: 1.0, leeway: .milliseconds(100))
timer.schedule(deadline: .now(), repeating: 1.0, leeway: .seconds(1))
timer.schedule(deadline: .now(), repeating: 1.0, leeway: .never)  // Strict — high energy
Leeway is the DispatchSourceTimer equivalent of
Timer.tolerance
. Allows system to coalesce timer firings for energy efficiency.
swift
// 误差值设置
timer.schedule(deadline: .now(), repeating: 1.0, leeway: .milliseconds(100))
timer.schedule(deadline: .now(), repeating: 1.0, leeway: .seconds(1))
timer.schedule(deadline: .now(), repeating: 1.0, leeway: .never)  // 严格计时——能耗高
Leeway是
Timer.tolerance
在DispatchSourceTimer中的等效设置,允许系统合并定时器触发以提升能效。

End-to-End Example

完整示例

Complete DispatchSourceTimer lifecycle in one block:
swift
let queue = DispatchQueue(label: "com.app.polling")
let timer = DispatchSource.makeTimerSource(queue: queue)
timer.schedule(deadline: .now() + 1.0, repeating: .seconds(5), leeway: .milliseconds(500))
timer.setEventHandler { [weak self] in
    self?.fetchUpdates()
}
timer.activate()  // idle → running

// Later — pause:
timer.suspend()   // running → suspended

// Later — resume:
timer.resume()    // suspended → running

// Cleanup — MUST resume before cancel if suspended:
timer.setEventHandler(handler: nil)  // Break retain cycles
timer.resume()    // Ensure non-suspended state
timer.cancel()    // running → cancelled (terminal)
For a safe wrapper that prevents all crash patterns, see
axiom-timer-patterns
Part 4: SafeDispatchTimer.

包含DispatchSourceTimer完整生命周期的代码块:
swift
let queue = DispatchQueue(label: "com.app.polling")
let timer = DispatchSource.makeTimerSource(queue: queue)
timer.schedule(deadline: .now() + 1.0, repeating: .seconds(5), leeway: .milliseconds(500))
timer.setEventHandler { [weak self] in
    self?.fetchUpdates()
}
timer.activate()  // 闲置→运行

// 后续操作——暂停:
timer.suspend()   // 运行→暂停

// 后续操作——恢复:
timer.resume()    // 暂停→运行

// 清理——若处于暂停状态,必须先恢复再取消:
timer.setEventHandler(handler: nil)  // 打破循环引用
timer.resume()    // 确保处于非暂停状态
timer.cancel()    // 运行→已取消(终态)
如需避免所有崩溃场景的安全封装,请参考
axiom-timer-patterns
第四部分:SafeDispatchTimer。

Part 3: Combine Timer

第三部分:Combine 定时器

Timer.publish

Timer.publish

swift
import Combine

// Create publisher — RunLoop mode matters here too
let publisher = Timer.publish(
    every: 1.0,          // Interval
    tolerance: 0.1,      // Optional tolerance
    on: .main,           // RunLoop
    in: .common          // Mode — use .common to survive scrolling
)
swift
import Combine

// 创建发布者——RunLoop模式在此处同样重要
let publisher = Timer.publish(
    every: 1.0,          // 时间间隔
    tolerance: 0.1,      // 可选误差
    on: .main,           // RunLoop
    in: .common          // 模式——使用.common可在滚动时保持触发
)

.autoconnect()

.autoconnect()

swift
// Starts immediately when first subscriber attaches
Timer.publish(every: 1.0, on: .main, in: .common)
    .autoconnect()
    .sink { date in
        print("Fired at \(date)")
    }
    .store(in: &cancellables)
swift
// 当第一个订阅者连接时立即启动
Timer.publish(every: 1.0, on: .main, in: .common)
    .autoconnect()
    .sink { date in
        print("触发时间:\(date)")
    }
    .store(in: &cancellables)

.connect() (Manual Start)

.connect()(手动启动)

swift
// Manual control over when timer starts
let timerPublisher = Timer.publish(every: 1.0, on: .main, in: .common)
let cancellable = timerPublisher
    .sink { date in
        print("Fired at \(date)")
    }

// Start later
let connection = timerPublisher.connect()

// Stop
connection.cancel()
swift
// 手动控制定时器启动时机
let timerPublisher = Timer.publish(every: 1.0, on: .main, in: .common)
let cancellable = timerPublisher
    .sink { date in
        print("触发时间:\(date)")
    }

// 稍后启动
let connection = timerPublisher.connect()

// 停止
connection.cancel()

Cancellation

取消

swift
// Via AnyCancellable storage — cancelled when Set is cleared or object deallocs
private var cancellables = Set<AnyCancellable>()

// Manual cancellation
cancellables.removeAll()  // Cancels all subscriptions
swift
// 通过AnyCancellable存储集合取消——当集合清空或对象释放时自动取消
private var cancellables = Set<AnyCancellable>()

// 手动取消
cancellables.removeAll()  // 取消所有订阅

SwiftUI Integration

SwiftUI 集成

swift
class TimerViewModel: ObservableObject {
    @Published var elapsed: Int = 0
    private var cancellables = Set<AnyCancellable>()

    func start() {
        Timer.publish(every: 1.0, tolerance: 0.1, on: .main, in: .common)
            .autoconnect()
            .sink { [weak self] _ in
                self?.elapsed += 1
            }
            .store(in: &cancellables)
    }

    func stop() {
        cancellables.removeAll()
    }
}

swift
class TimerViewModel: ObservableObject {
    @Published var elapsed: Int = 0
    private var cancellables = Set<AnyCancellable>()

    func start() {
        Timer.publish(every: 1.0, tolerance: 0.1, on: .main, in: .common)
            .autoconnect()
            .sink { [weak self] _ in
                self?.elapsed += 1
            }
            .store(in: &cancellables)
    }

    func stop() {
        cancellables.removeAll()
    }
}

Part 4: AsyncTimerSequence (Swift Concurrency)

第四部分:AsyncTimerSequence(Swift并发)

ContinuousClock.timer

ContinuousClock.timer

swift
// Monotonic clock — does NOT pause when app suspends
for await _ in ContinuousClock().timer(interval: .seconds(1)) {
    await updateData()
}
// Loop exits when task is cancelled
swift
// 单调时钟——应用暂停时不会停止计时
for await _ in ContinuousClock().timer(interval: .seconds(1)) {
    await updateData()
}
// 任务取消时循环退出

SuspendingClock.timer

SuspendingClock.timer

swift
// Suspending clock — pauses when app suspends
for await _ in SuspendingClock().timer(interval: .seconds(1)) {
    await processItem()
}
ContinuousClock vs SuspendingClock:
  • ContinuousClock
    : Time keeps advancing during app suspension. Use for absolute timing.
  • SuspendingClock
    : Time pauses when app suspends. Use for "user-perceived" timing.
swift
// 可暂停时钟——应用暂停时计时也会暂停
for await _ in SuspendingClock().timer(interval: .seconds(1)) {
    await processItem()
}
ContinuousClock vs SuspendingClock
  • ContinuousClock
    :应用暂停时时间仍会推进,适用于绝对计时场景。
  • SuspendingClock
    :应用暂停时计时暂停,适用于「用户感知层面」的计时场景。

Task Cancellation

任务取消

swift
// Timer automatically stops when task is cancelled
let timerTask = Task {
    for await _ in ContinuousClock().timer(interval: .seconds(1)) {
        await fetchLatestData()
    }
}

// Later: cancel the timer
timerTask.cancel()
swift
// 任务取消时定时器自动停止
let timerTask = Task {
    for await _ in ContinuousClock().timer(interval: .seconds(1)) {
        await fetchLatestData()
    }
}

// 稍后操作:取消定时器
timerTask.cancel()

Background Polling with Structured Concurrency

结构化并发下的后台轮询

swift
func startPolling() async {
    do {
        for try await _ in ContinuousClock().timer(interval: .seconds(30)) {
            try Task.checkCancellation()
            let data = try await api.fetchUpdates()
            await MainActor.run { updateUI(with: data) }
        }
    } catch is CancellationError {
        // Clean exit
    } catch {
        // Handle fetch error
    }
}

swift
func startPolling() async {
    do {
        for try await _ in ContinuousClock().timer(interval: .seconds(30)) {
            try Task.checkCancellation()
            let data = try await api.fetchUpdates()
            await MainActor.run { updateUI(with: data) }
        }
    } catch is CancellationError {
        // 正常退出
    } catch {
        // 处理请求错误
    }
}

Part 5: Task.sleep Alternatives

第五部分:Task.sleep 替代方案

One-Shot Delay

一次性延迟

swift
// Simple delay — NOT a timer
try await Task.sleep(for: .seconds(1))

// Deadline-based
try await Task.sleep(until: .now + .seconds(1), clock: .continuous)
swift
// 简单延迟——不属于定时器
try await Task.sleep(for: .seconds(1))

// 基于截止时间的延迟
try await Task.sleep(until: .now + .seconds(1), clock: .continuous)

When to Use Sleep vs Timer

Sleep与Timer的适用场景对比

NeedUse
One-shot delay before action
Task.sleep(for:)
Repeating action
ContinuousClock().timer(interval:)
Delay with cancellation
Task.sleep(for:)
in a Task
Retry with backoff
Task.sleep(for:)
in a loop
需求推荐方案
执行操作前的一次性延迟
Task.sleep(for:)
重复执行操作
ContinuousClock().timer(interval:)
支持取消的延迟在Task中使用
Task.sleep(for:)
带退避策略的重试在循环中使用
Task.sleep(for:)

Retry with Exponential Backoff

指数退避重试

swift
func fetchWithRetry(maxAttempts: Int = 3) async throws -> Data {
    var delay: Duration = .seconds(1)
    for attempt in 1...maxAttempts {
        do {
            return try await api.fetch()
        } catch where attempt < maxAttempts {
            try await Task.sleep(for: delay)
            delay *= 2  // Exponential backoff
        }
    }
    throw FetchError.maxRetriesExceeded
}

swift
func fetchWithRetry(maxAttempts: Int = 3) async throws -> Data {
    var delay: Duration = .seconds(1)
    for attempt in 1...maxAttempts {
        do {
            return try await api.fetch()
        } catch where attempt < maxAttempts {
            try await Task.sleep(for: delay)
            delay *= 2  // 指数退避
        }
    }
    throw FetchError.maxRetriesExceeded
}

Part 6: LLDB Timer Inspection

第六部分:LLDB 定时器调试

Timer (NSTimer) Commands

Timer(NSTimer)命令

lldb
undefined
lldb
undefined

Check if timer is still valid

检查定时器是否仍有效

po timer.isValid
po timer.isValid

See next fire date

查看下一次触发时间

po timer.fireDate
po timer.fireDate

See timer interval

查看定时器时间间隔

po timer.timeInterval
po timer.timeInterval

Force RunLoop iteration (may trigger timer)

强制触发RunLoop迭代(可能会触发定时器)

expression -l objc -- (void)[[NSRunLoop mainRunLoop] run]
undefined
expression -l objc -- (void)[[NSRunLoop mainRunLoop] run]
undefined

DispatchSourceTimer Commands

DispatchSourceTimer 命令

lldb
undefined
lldb
undefined

Inspect dispatch source

检查调度源

po timer
po timer

Break on dispatch source cancel (all sources)

在所有调度源取消时触发断点

breakpoint set -n dispatch_source_cancel
breakpoint set -n dispatch_source_cancel

Break on EXC_BAD_INSTRUCTION to catch timer crashes

在触发EXC_BAD_INSTRUCTION时断点以捕获定时器崩溃

(Xcode does this automatically for Swift runtime errors)

(Xcode会自动为Swift运行时错误设置此断点)

Check if a DispatchSource is cancelled

检查DispatchSource是否已取消

expression -l objc -- (long)dispatch_source_testcancel((void*)timer)
undefined
expression -l objc -- (long)dispatch_source_testcancel((void*)timer)
undefined

General Timer Debugging

通用定时器调试

lldb
undefined
lldb
undefined

List all timers on the main RunLoop

列出主RunLoop上的所有定时器

expression -l objc -- (void)CFRunLoopGetMain()
expression -l objc -- (void)CFRunLoopGetMain()

Break when any Timer fires

任何Timer触发时触发断点

breakpoint set -S "scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:"

---
breakpoint set -S "scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:"

---

Part 7: Platform Availability Matrix

第七部分:平台兼容性矩阵

APIiOSmacOSwatchOStvOS
Timer2.0+10.0+2.0+9.0+
DispatchSourceTimer8.0+ (GCD)10.10+2.0+9.0+
Timer.publish (Combine)13.0+10.15+6.0+13.0+
AsyncTimerSequence16.0+13.0+9.0+16.0+
Task.sleep13.0+10.15+6.0+13.0+

APIiOSmacOSwatchOStvOS
Timer2.0+10.0+2.0+9.0+
DispatchSourceTimer8.0+ (GCD)10.10+2.0+9.0+
Timer.publish (Combine)13.0+10.15+6.0+13.0+
AsyncTimerSequence16.0+13.0+9.0+16.0+
Task.sleep13.0+10.15+6.0+13.0+

Related Skills

相关技能

  • axiom-timer-patterns
    — Decision trees, crash patterns, SafeDispatchTimer wrapper
  • axiom-energy
    — Timer tolerance as energy optimization (Pattern 1)
  • axiom-energy-ref
    — Timer efficiency APIs with WWDC code examples
  • axiom-memory-debugging
    — Timer as Pattern 1 memory leak
  • axiom-timer-patterns
    —— 决策树、崩溃场景、SafeDispatchTimer封装
  • axiom-energy
    —— 定时器误差作为能耗优化手段(模式1)
  • axiom-energy-ref
    —— 定时器效率API及WWDC代码示例
  • axiom-memory-debugging
    —— 定时器作为模式1内存泄漏场景

Resources

资源

Skills: axiom-timer-patterns, axiom-energy-ref, axiom-memory-debugging
技能:axiom-timer-patterns, axiom-energy-ref, axiom-memory-debugging