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

Swift中如何持续更新常量值?加载器无限循环问题求解

问题一:如何在Swift中持续更新一个常量值?

在Swift里,用let声明的常量是天生不可变的——一旦完成初始化,就再也没法修改它的值。如果你的需求是需要一个能持续更新、但又想限制外部修改权限的“类常量”,可以试试这些替代方案:

  • 私有变量+只读计算属性:把可变的逻辑藏在内部,只给外部暴露只读的接口,既满足内部更新需求,又能避免外部随意修改:
    class ImageLoader {
        private var _loadingCount: Int = 0
        // 外部只能读,不能改
        var isLoading: Bool {
            return _loadingCount > 0
        }
    
        func imageStartedLoading() {
            _loadingCount += 1
        }
    
        func imageFinishedLoading() {
            _loadingCount -= 1
        }
    }
    
  • 属性包装器(Property Wrapper):如果需要更复杂的更新逻辑(比如更新时触发通知),可以自定义属性包装器,把“可更新但受控制”的逻辑封装起来,用起来像常量但内部能修改。

划重点:Swift的let设计就是为了保证值的不可变性,没有直接修改常量的“黑科技”,别硬钻牛角尖,用var配合逻辑控制才是正道。


问题二:解决Loader条件判断无限循环的问题

你的代码问题太明显了——loadingImages()里无条件递归调用自己!不管conditions是true还是false,只要conditions不是nil,就会再次调用loadingImages(),直接把UI线程卡死,根本没机会检测图片是否真的加载完成。

给你两个靠谱的解决方案,按需选:

方案一:定时轮询检测(简单易上手)

用GCD定时器每隔一小段时间检查一次条件,避免同步递归阻塞线程:

var checkTimer: Timer?

func startLoadingCheck() {
    loader.startLoader(view: view)
    // 每隔0.1秒检查一次图片状态,时间间隔可以根据需求调整
    checkTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] timer in
        guard let self = self else {
            timer.invalidate()
            return
        }
        // 检查所有按钮的图片是否加载完成
        let allImagesLoaded = [self.button1, self.button2, self.button3,
                               self.button4, self.button5, self.button6,
                               self.button7, self.button8, self.button9]
            .allSatisfy { $0.currentImage != nil }
        
        if allImagesLoaded {
            // 所有图片加载完成,停止Loader和定时器
            self.loader.stop()
            timer.invalidate()
            self.checkTimer = nil
        }
    }
}

调用的时候直接用startLoadingCheck()代替原来的loadingImages()就行,这样不会卡UI,每次定时器触发都会检查状态,一旦全部加载完成就立即停止Loader。

方案二:监听图片加载回调(更高效)

如果你的图片是异步加载的(比如网络图片),别用轮询,直接给每个图片的加载任务加完成回调,等最后一张加载完就停Loader:

func loadAllImages() {
    loader.startLoader(view: view)
    var loadedImageCount = 0
    let totalImages = 9 // 对应你的9个按钮
    
    // 检查是否全部加载完成的逻辑
    func checkCompletion() {
        if loadedImageCount == totalImages {
            loader.stop()
        }
    }
    
    // 封装图片加载方法,带完成回调
    func loadImage(for button: UIButton, with url: URL) {
        URLSession.shared.dataTask(with: url) { data, _, error in
            DispatchQueue.main.async {
                if let data = data, let image = UIImage(data: data) {
                    button.setImage(image, for: .normal)
                }
                // 不管成功失败都计数,避免无限等待
                loadedImageCount += 1
                checkCompletion()
            }
        }.resume()
    }
    
    // 加载每个按钮的图片
    loadImage(for: button1, with: yourImageURL1)
    loadImage(for: button2, with: yourImageURL2)
    // ... 剩下的按钮依次调用
}

这种方式不需要轮询,图片加载完成后主动通知,响应更快,也不浪费资源,是最优解。

为啥你的原代码会无限循环?

原代码里,if let cond = conditions只要conditions不是nil就会执行,而你每次调用loadingImages()都会给conditions赋值(不管是true还是false),所以永远会进入这个分支,递归调用自己,根本走不到else分支去停止Loader,直接导致栈溢出或者UI卡死。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 18:37:33