Timer Patterns Reference
定时器模式参考
Complete API reference for iOS timer mechanisms. For decision trees and crash prevention, see
.
iOS定时器机制的完整API参考。如需决策树和崩溃预防相关内容,请参考
。
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
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()
}
关键细节:添加到
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
. If
also holds the timer, you have a retain cycle. The block-based API with
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会强引用
。如果
同时持有timer,会形成循环引用。使用
的闭包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唤醒次数,降低能耗。
| Mode | Constant | When Active | Timer Fires? |
|---|
| Default | / | Normal user interaction | Yes |
| Tracking | / | Scroll/drag gesture active | Only if added to |
| Common | / | Pseudo-mode (default + tracking) | Yes (always) |
| 模式 | 常量 | 激活场景 | 定时器是否触发? |
|---|
| 默认 | / | 常规用户交互时 | 是 |
| 追踪 | / | 滚动/拖拽手势激活时 | 仅当添加到模式时触发 |
| 通用 | / | 伪模式(默认+追踪) | 始终触发 |
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:
must be called from the same thread that created the timer (usually main thread).
swift
timer.invalidate() // 停止定时器,从RunLoop中移除
// 调用invalidate()后定时器无法复用——需创建新的定时器
timer = nil // 释放引用
关键细节:
必须在创建定时器的同一线程调用(通常为主线程)。
timer.isValid
timer.isValid
swift
if timer.isValid {
// Timer is still active
}
Returns
after
or after a non-repeating timer fires.
swift
if timer.isValid {
// 定时器仍处于激活状态
}
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
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
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:通常为空数组
。使用
可实现精确计时(禁用系统合并机制,能耗更高)。
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:
uses monotonic clock (pauses when device sleeps).
uses wall clock (continues across sleep). Use
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:
使用单调时钟(设备休眠时暂停计时)。
使用挂钟(设备休眠时仍继续计时)。大多数场景下使用
即可。
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()
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:
- can only be called once (idle → running)
- requires non-suspended state (resume first if suspended)
- is terminal — no further operations allowed
- Dealloc requires non-suspended state (cancel first if needed)
activate()
idle ──────────────► running
│ ▲
suspend() │ │ resume()
▼ │
suspended
│
resume() + 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
. 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是
在DispatchSourceTimer中的等效设置,允许系统合并定时器触发以提升能效。
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
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() // 运行→已取消(终态)
如需避免所有崩溃场景的安全封装,请参考
第四部分: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()
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:
- : Time keeps advancing during app suspension. Use for absolute timing.
- : Time pauses when app suspends. Use for "user-perceived" timing.
swift
// 可暂停时钟——应用暂停时计时也会暂停
for await _ in SuspendingClock().timer(interval: .seconds(1)) {
await processItem()
}
ContinuousClock vs SuspendingClock:
- :应用暂停时时间仍会推进,适用于绝对计时场景。
- :应用暂停时计时暂停,适用于「用户感知层面」的计时场景。
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 替代方案
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的适用场景对比
| Need | Use |
|---|
| One-shot delay before action | |
| Repeating action | ContinuousClock().timer(interval:)
|
| Delay with cancellation | in a Task |
| Retry with backoff | in a loop |
| 需求 | 推荐方案 |
|---|
| 执行操作前的一次性延迟 | |
| 重复执行操作 | ContinuousClock().timer(interval:)
|
| 支持取消的延迟 | 在Task中使用 |
| 带退避策略的重试 | 在循环中使用 |
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)命令
Check if timer is still valid
检查定时器是否仍有效
See next fire date
查看下一次触发时间
See timer interval
查看定时器时间间隔
Force RunLoop iteration (may trigger timer)
强制触发RunLoop迭代(可能会触发定时器)
expression -l objc -- (void)[[NSRunLoop mainRunLoop] run]
expression -l objc -- (void)[[NSRunLoop mainRunLoop] run]
DispatchSourceTimer Commands
DispatchSourceTimer 命令
Inspect dispatch source
检查调度源
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)
expression -l objc -- (long)dispatch_source_testcancel((void*)timer)
General Timer Debugging
通用定时器调试
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
第七部分:平台兼容性矩阵
| API | iOS | macOS | watchOS | tvOS |
|---|
| Timer | 2.0+ | 10.0+ | 2.0+ | 9.0+ |
| DispatchSourceTimer | 8.0+ (GCD) | 10.10+ | 2.0+ | 9.0+ |
| Timer.publish (Combine) | 13.0+ | 10.15+ | 6.0+ | 13.0+ |
| AsyncTimerSequence | 16.0+ | 13.0+ | 9.0+ | 16.0+ |
| Task.sleep | 13.0+ | 10.15+ | 6.0+ | 13.0+ |
| API | iOS | macOS | watchOS | tvOS |
|---|
| Timer | 2.0+ | 10.0+ | 2.0+ | 9.0+ |
| DispatchSourceTimer | 8.0+ (GCD) | 10.10+ | 2.0+ | 9.0+ |
| Timer.publish (Combine) | 13.0+ | 10.15+ | 6.0+ | 13.0+ |
| AsyncTimerSequence | 16.0+ | 13.0+ | 9.0+ | 16.0+ |
| Task.sleep | 13.0+ | 10.15+ | 6.0+ | 13.0+ |
- — Decision trees, crash patterns, SafeDispatchTimer wrapper
- — Timer tolerance as energy optimization (Pattern 1)
- — Timer efficiency APIs with WWDC code examples
- — Timer as Pattern 1 memory leak
- —— 决策树、崩溃场景、SafeDispatchTimer封装
- —— 定时器误差作为能耗优化手段(模式1)
- —— 定时器效率API及WWDC代码示例
- —— 定时器作为模式1内存泄漏场景
Skills: axiom-timer-patterns, axiom-energy-ref, axiom-memory-debugging
技能:axiom-timer-patterns, axiom-energy-ref, axiom-memory-debugging