Swift中的速率限制:iOS和macOS应用完整指南
速率限制(请求频率限制)是确保移动和桌面应用稳定运行的关键机制。在Swift(iOS/macOS)生态系统中,这种方法可以控制单位时间内API调用、I/O操作或任何重复操作的数量。如果没有速率限制,您的应用可能会被服务器封锁,因无限重试而耗尽电池电量,或对网络造成过大负载。
为什么需要在Swift中限制请求频率?
- 防止超出API限制 — 许多服务器(例如GitHub、Twitter、Stripe)对每分钟/小时的请求数量设有严格配额。
- 优化性能 — 防止快速滚动或动画时出现调用“风暴”。
- 节省流量和电池 — 对移动设备尤为重要。
- 遵守App Store政策 — 过多的后台请求可能导致应用被拒绝。
Swift中实现速率限制的主要方法
让我们看看三种无需第三方库即可实现的基本模式:
1. 令牌桶(Token Bucket)
经典算法,其中“令牌”以固定速率添加。每个请求消耗一个令牌。如果令牌用完,请求将被拒绝。
import Foundation
final class TokenBucket { private let capacity: Int private var tokens: Int private let refillRate: Double // 每秒令牌数 private var lastRefill: Date private let queue = DispatchQueue(label: "token.bucket.queue") init(capacity: Int, refillRate: Double) { self.capacity = capacity self.tokens = capacity self.refillRate = refillRate self.lastRefill = Date() } func tryConsume() -> Bool { queue.sync { refill() if tokens > 0 { tokens -= 1 return true } return false } } private func refill() { let now = Date() let elapsed = now.timeIntervalSince(lastRefill) let newTokens = Int(elapsed * refillRate) if newTokens > 0 { tokens = min(capacity, tokens + newTokens) lastRefill = now } }}
// 使用示例let limiter = TokenBucket(capacity: 10, refillRate: 2.0) // 10个请求,每秒2个if limiter.tryConsume() { print("✅ 请求已允许")} else { print("⛔️ 超出限制,请等待")}2. 滑动窗口算法(Sliding Window)
记录最近N个成功请求的时间戳。如果窗口已满,则阻止新调用。
final class SlidingWindowLimiter { private let maxRequests: Int private let windowDuration: TimeInterval private var timestamps: [Date] = [] private let queue = DispatchQueue(label: "sliding.window.queue") init(maxRequests: Int, windowDuration: TimeInterval = 60) { self.maxRequests = maxRequests self.windowDuration = windowDuration } func canProceed() -> Bool { queue.sync { let now = Date() // 移除过期的时间戳 timestamps.removeAll { now.timeIntervalSince($0) > windowDuration } if timestamps.count < maxRequests { timestamps.append(now) return true } return false } }}
// 示例:10秒内不超过5个请求let windowLimiter = SlidingWindowLimiter(maxRequests: 5, windowDuration: 10)3. 使用DispatchQueue进行节流
利用GCD内置机制强制延迟