iOS开发:两个ViewController间传递双精度数值并维持计数
解决iOS ViewController间持续计数的数据同步问题
嘿,我明白你要做的事了——在ViewController1和ViewController2之间切换时,让计数器持续运转,而且两个页面都能获取到实时的计数数值对吧?直接在prepare(for:sender:)里传值肯定行不通,因为Double是值类型,传过去的只是当前counter的副本,后续VC1里的counter更新不会同步到VC2;而且如果VC1被销毁,计时器也会跟着停止。下面给你几个可行的方案:
方案一:用单例管理计数器与计时器(最直观的方案)
我们可以把计数器和计时器的逻辑抽离到一个单例类里,这样不管是VC1还是VC2,访问的都是同一个数据源,计时器的生命周期也不依赖单个ViewController。
1. 创建单例管理类
class CounterManager { // 单例实例,全局唯一 static let shared = CounterManager() // 对外只读的计数器,只能内部修改 private(set) var counter: Double = 0.00 private var timer: Timer? // 私有初始化方法,防止外部创建实例 private init() {} // 启动计时器 func startTimer() { // 避免重复启动 guard timer == nil else { return } timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true) } // 停止计时器 func stopTimer() { timer?.invalidate() timer = nil } // 内部更新计数器的方法 @objc private func updateCounter() { counter += 0.1 // 发送通知,通知所有监听页面更新UI NotificationCenter.default.post(name: NSNotification.Name("CounterUpdated"), object: counter) } }
2. 修改ViewController1的代码
class ViewController1: UIViewController { override func viewDidLoad() { super.viewDidLoad() // 进入页面就启动计时器 CounterManager.shared.startTimer() } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let vc2 = segue.destination as? ViewController2 { // 给VC2传递当前的初始计数器值 vc2.currentCounter = CounterManager.shared.counter } } deinit { // 可选:如果退出VC1后不需要继续计数,可以在这里停止计时器 // CounterManager.shared.stopTimer() } }
3. 修改ViewController2的代码
class ViewController2: UIViewController { // 接收初始的计数器值 var currentCounter: Double = 0.00 // 保存通知监听的引用,用于销毁时移除 private var notificationObserver: NSObjectProtocol? // 假设你有一个显示计数器的UILabel @IBOutlet weak var counterLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() // 初始化显示当前计数器值 updateCounterLabel() // 监听计数器更新的通知,实时同步数值 notificationObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name("CounterUpdated"), object: nil, queue: .main) { [weak self] notification in guard let newCounter = notification.object as? Double else { return } self?.currentCounter = newCounter self?.updateCounterLabel() } } // 更新UI的方法 private func updateCounterLabel() { counterLabel.text = String(format: "%.2f", currentCounter) } deinit { // 销毁页面时移除通知监听,避免内存泄漏 if let observer = notificationObserver { NotificationCenter.default.removeObserver(observer) } } }
方案二:用Combine框架实现响应式同步(Swift 5+推荐)
如果你的项目用的是Swift 5及以上版本,Combine框架能更优雅地实现数据同步,不需要手动管理通知:
1. 修改单例类(引入Combine)
import Combine class CounterManager { static let shared = CounterManager() // 使用@Published包装计数器,自动发送更新通知 @Published var counter: Double = 0.00 private var timer: Timer? private init() {} func startTimer() { guard timer == nil else { return } timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in self?.counter += 0.1 } } func stopTimer() { timer?.invalidate() timer = nil } }
2. 修改ViewController2的代码
import Combine class ViewController2: UIViewController { @IBOutlet weak var counterLabel: UILabel! // 保存订阅引用,防止被提前释放 private var cancellables = Set<AnyCancellable>() override func viewDidLoad() { super.viewDidLoad() // 订阅计数器的更新,实时同步到UI CounterManager.shared.$counter .receive(on: DispatchQueue.main) // 确保在主线程更新UI .sink { [weak self] newCounter in self?.counterLabel.text = String(format: "%.2f", newCounter) } .store(in: &cancellables) } }
注意事项
- 记得在合适的时机停止计时器(比如应用进入后台、用户退出计数页面时),避免不必要的资源消耗和内存泄漏。
- 使用闭包或通知时,一定要用
[weak self]避免循环引用。
内容的提问来源于stack exchange,提问作者Leon
相关产品推荐
相关产品推荐

