Swift iOS:实现串行函数执行,解决多函数并行执行问题
解决方案:用Completion回调+递归实现顺序执行与重复
你的问题核心在于Timer的异步特性——Timer.scheduledTimer是把任务调度到RunLoop里异步执行的,不会阻塞当前代码的运行。所以你调用timer1()之后,代码会立刻跳到timer2()执行,直接把背景色改成绿色,而且两个Timer还会互相覆盖全局的timer变量,完全打乱了预期逻辑。
这里推荐用Completion Handler(完成回调)+递归的方案,既保证步骤严格顺序执行,又能轻松扩展后续需求,代码也更易维护。
优化后的完整代码
import UIKit class ViewController: UIViewController { // 把流程步骤抽成可配置的结构体,方便后续扩展修改 struct SequenceStep { let color: UIColor let duration: Int // 扩展:支持每一秒的自定义动作(比如第n秒播放音效) let onTick: ((Int) -> Void)? init(color: UIColor, duration: Int, onTick: ((Int) -> Void)? = nil) { self.color = color self.duration = duration self.onTick = onTick } } // 颜色定义 private let redColor = UIColor(red: 255/255.0, green: 0/255.0, blue: 0/255.0, alpha: 1.0) private let greenColor = UIColor(red: 0/255.0, green: 255/255.0, blue: 0/255.0, alpha: 1.0) // 配置完整流程:后续加步骤直接在这里加就行 private let fullSequence: [SequenceStep] = [ SequenceStep(color: .red, duration: 30, onTick: { remainingSeconds in // 示例:第4秒播放音效(30-4=26,剩余26秒时触发) if remainingSeconds == 26 { print("播放提示音效!") // 这里添加播放音效的代码即可 } }), SequenceStep(color: .green, duration: 10) ] private var currentTimer: Timer? private var repeatCount = 0 private let totalRepeatTimes = 3 // 总共重复3次 @IBOutlet weak var startButton: UIButton! @IBAction func startSequence(_ sender: Any) { startButton.isHidden = true repeatCount = 0 startNextRepeatCycle() } // 启动一轮完整的流程循环 private func startNextRepeatCycle() { guard repeatCount < totalRepeatTimes else { // 所有循环完成,可添加结束逻辑 print("全部流程执行完毕") startButton.isHidden = false // 可选:重新显示开始按钮 return } repeatCount += 1 executeStep(atIndex: 0) } // 按顺序执行单个步骤,完成后自动触发下一个 private func executeStep(atIndex index: Int) { guard index < fullSequence.count else { // 一轮步骤完成,启动下一次循环 startNextRepeatCycle() return } let step = fullSequence[index] view.backgroundColor = step.color var remainingSeconds = step.duration currentTimer?.invalidate() // 先销毁之前的定时器,避免冲突 currentTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in guard let self = self else { timer.invalidate() return } // 执行当前步骤的自定义tick动作 step.onTick?(remainingSeconds) remainingSeconds -= 1 if remainingSeconds < 0 { timer.invalidate() self.currentTimer = nil // 执行下一个步骤 self.executeStep(atIndex: index + 1) } } } }
这个方案的优势
- 严格顺序执行:每个步骤完全完成后才会触发下一个,彻底解决同时执行的问题。
- 超强扩展性:
- 加新步骤:直接在
fullSequence数组里添加新的SequenceStep实例。 - 加中间动作:在
SequenceStep的onTick回调里判断剩余秒数即可实现(比如第4秒播放音效)。 - 修改时长/颜色:直接修改
SequenceStep的参数,不用动核心执行逻辑。
- 加新步骤:直接在
- 清晰的重复逻辑:用递归的
startNextRepeatCycle控制重复次数,逻辑一目了然。 - 避免全局变量混乱:把定时器和计数改成实例变量,防止被意外覆盖。
额外注意点
- 用
[weak self]避免循环引用,防止内存泄漏。 - 每次启动新定时器前先销毁旧的,避免多个定时器同时运行。
- 可以把
totalRepeatTimes、fullSequence做成可配置的变量,支持动态修改。
内容的提问来源于stack exchange,提问作者Darryl Mitchell
相关产品推荐
相关产品推荐

