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

Grand Central Dispatch代码应放置在何处以实现后台持续运行?

解决后台持续运行计数器并更新UI的问题

先聊聊你当前代码里几个需要注意的细节,再告诉你该把GCD代码放在哪里,以及更合理的实现方式:

一、现有代码的潜在问题

  • UI更新必须在主线程:你在后台队列里直接修改X.text,这会触发UIKit的线程安全问题,可能导致界面卡顿或异常——所有UI操作都必须在主线程执行。
  • sleep(1)阻塞线程:用sleep会持续占用后台队列的线程,既不高效还可能影响队列里其他任务的执行,不如用定时器更优雅。
  • 线程安全隐患:globalValues的读写如果在多线程环境下进行,会出现竞态条件(比如多个线程同时修改Bits值),需要加锁保证数据安全。
  • 无退出逻辑的循环:while placeHolder == 0没有终止条件,就算用户离开当前页面,这个线程也会一直跑,白白浪费系统资源。

二、GCD代码的放置位置

你需要在页面显示时启动后台任务,页面消失时停止任务,这样既能保证用户查看时持续更新,又能避免无效的后台运行:

  • 在视图控制器的viewDidAppear(_:)方法里启动后台任务
  • 在viewDidDisappear(_:)方法里停止任务,释放相关资源

三、优化后的实现示例

给你的视图控制器添加定时器属性来控制任务启停,同时修复线程安全和UI更新的问题:

class YourViewController: UIViewController {
    // 用于控制后台任务的定时器
    private var updateTimer: DispatchSourceTimer?
    // 假设你的计数标签是这个IBOutlet属性
    @IBOutlet weak var BitCount: UILabel!
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        startBackgroundCounter()
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        stopBackgroundCounter()
    }
    
    private func startBackgroundCounter() {
        // 创建后台并发队列
        let queue = DispatchQueue(label: "queue.1", qos: .utility, attributes: .concurrent)
        
        // 创建每秒触发一次的定时器
        updateTimer = DispatchSource.makeTimerSource(queue: queue)
        updateTimer?.schedule(deadline: .now(), repeating: .seconds(1))
        updateTimer?.setEventHandler { [weak self] in
            guard let self = self else { return }
            
            // 给globalValues加锁,保证线程安全
            objc_sync_enter(globalValues)
            var currentBits = globalValues.Bits
            if globalValues.bitsPerSecond != 0 {
                currentBits += 1
                globalValues.Bits = currentBits
                print("Ran")
                // 保存到UserDefaults
                UserDefaults.standard.set(currentBits, forKey: "bits")
            }
            objc_sync_exit(globalValues)
            
            // 回到主线程更新UI
            DispatchQueue.main.async {
                self.BitCount.text = "\(currentBits)"
            }
        }
        updateTimer?.resume()
    }
    
    private func stopBackgroundCounter() {
        updateTimer?.cancel()
        updateTimer = nil
    }
}

补充说明

  • 用DispatchSourceTimer代替while循环+sleep,更符合GCD的最佳实践,不会阻塞线程,资源占用更低。
  • 用objc_sync_enter/exit给globalValues加锁,你也可以用NSLock或者串行队列来处理线程安全问题。
  • [weak self]避免循环引用,确保视图控制器能正常被释放。
  • 在viewDidDisappear里停止定时器,保证用户离开页面后任务立即终止,节省系统资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:12:06