You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift MVC模式下结构体中Timer的正确实现方法

Swift MVC重构秒表Timer无法运行的解决方案

问题背景

Swift新手开发秒表应用,原代码全部放在ViewController中时Timer可正常运行;改用MVC模式重构,将Timer逻辑放入结构体FastBrain后报错:'weak'仅适用于类及类约束协议类型,而非'FastBrain',调整后仍无法正常启动Timer。

原ViewController可运行代码片段

fastTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { [weak self] timer in
            
    // Update the elapsed time variable
    self?.elapsedTime += timer.timeInterval
            
    // Format the elapsed time as a stopwatch time
    //calculate values for timer label for next run

    let hours = Int(self?.elapsedTime ?? 0) / 3600 % 60
    let minutes = Int(self?.elapsedTime ?? 0) / 60 % 60
    let seconds = Int(self?.elapsedTime ?? 0) % 60
            
    //if fast is completed don't recalculate progress bar
    if self?.progressBar.progress ?? 0.0 >= 1.0 {
        self?.progressBar.progress = 1
    } else {
        //calculate value for progress bar
        self?.progressBar.progress =  Float(self?.elapsedTime ?? 0.0) / self!.goalFastTimeSeconds
    }
            
    // Update the label with the formatted time
    self?.timeElapsedLabel.text = String(format: "%02d:%02d:%02d", hours, minutes, seconds)
})
        
// Update the button text as we've started fast
sender.setTitle("Finish Fast", for: .normal)

当前模型结构体FastBrain代码

import Foundation

struct FastBrain {
    
    var goalFastTimeHours = 1
    var goalFastTimeSeconds: Float = 3600
    var progress: Float = 0.0
    var fastTimer: Timer?
    var elapsedTime: TimeInterval = 0
    let startDate: Date = Date()
    let dateFormatter = DateFormatter()

    var hours = 0
    var minutes = 0
    var seconds = 0
   
 
    mutating func startFast(){
        
        //start the timer - except it won't :-(
        fastTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { [weak self] timer in
            
            // Update the elapsed time variable
            elapsedTime += fastTimer?.timeInterval ?? 0.0
            
            //calculate values for timer label for next run
            hours = Int(self?.elapsedTime ?? 0 ) / 3600 % 60
            minutes = Int(self?.elapsedTime ?? 0 ) / 60 % 60
            seconds = Int(self?.elapsedTime ?? 0) % 60
          
            
            //if fast is completed don't recalculate progress bar
            if progress >= 1.0 {
                progress = 1
            } else {
                //calculate value for progress bar
                progress =  Float(self?.elapsedTime ?? 0.0 ) / (self?.goalFastTimeSeconds ?? 0)
            }
            //How do I call this function from here?
            //  updateUIDuringFast()
            
        })//block ends here
    }
}

核心问题分析

  1. 结构体不能使用weak引用:FastBrain是值类型(结构体),[weak self]仅适用于引用类型(类),值类型不存在引用计数,weak无意义。
  2. 值类型状态更新失效:Timer闭包捕获结构体实例时,会创建副本,闭包内修改的是副本状态,无法同步到外部的原实例。
  3. 模型违反MVC原则:原代码在模型中尝试处理UI更新,MVC模式下模型应只负责业务逻辑,UI更新由ViewController处理。

修正方案

1. 将FastBrain改为类(引用类型)

把结构体改为类,解决weak self的类型限制,同时保证闭包修改的是同一个实例的状态:

import Foundation

class FastBrain {
    var goalFastTimeHours = 1
    var goalFastTimeSeconds: Float = 3600
    var progress: Float = 0.0
    var fastTimer: Timer?
    var elapsedTime: TimeInterval = 0
    let startDate: Date = Date()
    let dateFormatter = DateFormatter()

    var hours = 0
    var minutes = 0
    var seconds = 0
    
    // 定义回调闭包,用于通知ViewController更新UI
    var updateHandler: (() -> Void)?
   
    func startFast(){
        // 先销毁已有Timer,避免重复创建
        fastTimer?.invalidate()
        // 重置计时状态
        elapsedTime = 0
        progress = 0
        
        fastTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { [weak self] timer in
            guard let self = self else {
                timer.invalidate() // 实例已释放,停止Timer
                return
            }
            
            // 更新累计计时
            self.elapsedTime += timer.timeInterval
            
            // 计算时分秒
            self.hours = Int(self.elapsedTime) / 3600 % 60
            self.minutes = Int(self.elapsedTime) / 60 % 60
            self.seconds = Int(self.elapsedTime) % 60
            
            // 更新进度,完成后停止Timer
            if self.progress >= 1.0 {
                timer.invalidate()
                self.progress = 1
            } else {
                self.progress = Float(self.elapsedTime) / self.goalFastTimeSeconds
            }
            
            // 触发回调,通知UI更新
            self.updateHandler?()
        })
    }
    
    // 添加停止Timer的方法
    func stopFast() {
        fastTimer?.invalidate()
        fastTimer = nil
    }
}

2. ViewController中使用FastBrain并处理UI更新

在ViewController中初始化FastBrain实例,通过回调闭包处理UI更新,同时管理按钮交互:

class ViewController: UIViewController {
    @IBOutlet weak var timeElapsedLabel: UILabel!
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var startButton: UIButton!
    
    // 初始化FastBrain类实例
    let fastBrain = FastBrain()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 设置模型的回调,处理UI更新逻辑
        fastBrain.updateHandler = { [weak self] in
            guard let self = self else { return }
            // 更新时间显示标签
            self.timeElapsedLabel.text = String(format: "%02d:%02d:%02d", self.fastBrain.hours, self.fastBrain.minutes, self.fastBrain.seconds)
            // 更新进度条
            self.progressBar.progress = self.fastBrain.progress
            // 计时完成后更新按钮文字
            if self.fastBrain.progress >= 1.0 {
                self.startButton.setTitle("Restart Fast", for: .normal)
            }
        }
    }
    
    @IBAction func startButtonTapped(_ sender: UIButton) {
        if fastBrain.fastTimer?.isValid ?? false {
            // 当前正在计时,停止并切换按钮文字
            fastBrain.stopFast()
            sender.setTitle("Start Fast", for: .normal)
        } else {
            // 开始计时并切换按钮文字
            fastBrain.startFast()
            sender.setTitle("Finish Fast", for: .normal)
        }
    }
    
    // 页面消失时停止Timer,避免后台泄漏
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        fastBrain.stopFast()
    }
}

关键改进点

  • 将FastBrain改为类,解决weak self的类型问题,保证状态修改的一致性。
  • 新增updateHandler回调,让模型专注业务逻辑,UI更新由ViewController处理,符合MVC原则。
  • 添加stopFast方法,避免Timer重复创建和后台资源泄漏。
  • 在Timer闭包中增加guard let self = self,安全处理实例释放的情况,避免野指针访问。

内容的提问来源于stack exchange,提问作者CatherineLeBlanc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 05:35:02