Swift图片压缩任务队列调度问题
Swift图片压缩任务队列调度问题

Swift图片压缩任务队列调度问题

问题诉求

我想要实现在macOS应用中,上传图片后进入压缩任务。

当我前面上传的图片还在压缩中,新的图片被上传。

如果调用压缩方法,压缩上传的图片,必定会导致重复压缩的场景,因此就需要实现一个经典的任务队列调度问题。

因此,就需要实现一个高效、有序、稳定的图片压缩流程,需要考虑的因素有:

1、压缩是异步耗时操作。

2、用户可能随时添加图片。

3、了解每张图片的状态(未压缩 / 压缩中 / 已完成 / 失败)。

队列模型

可以使用任务队列+状态管理解决这一问题:

1、为每张图片维护状态

为每张图片模型加一个枚举字段 compressionState压缩状态:

enum CompressionState {
    case pending     // 等待压缩
    case compressing // 正在压缩
    case completed   // 已压缩完成
    case failed      // 压缩失败
}

可以根据图片的compressionState字段,了解图片当前的压缩状态。

2、设计一个图片压缩任务队列

使用一个串行队列(可选带优先级)来顺序处理压缩任务:

import SwiftUI

class CompressionManager:ObservableObject {
    static let shared = CompressionManager()
    // 任务队列:存储被压缩的图片
    private var taskQueue: [CustomImages] = []
    
    // 当前有无被压缩的图片,isCompressing表示当前有图片被压缩,其他图片需要等待
    private var isCompressing = false
    
    // 进入压缩队列,开始压缩
    func enqueue(_ image: CustomImages) {
        guard image.compressionState == .pending else { return } // 防止重复压缩
        // 将图片添加到任务队列
        taskQueue.append(image)
        compressionTask()
    }

    // 压缩任务:
    // 1、判断压缩和任务队列
    // 2、修改当前压缩状态和图片的压缩状态
    // 3、当压缩成功后,修改图片的压缩状态,移除任务队列中已经压缩图片,将当前压缩状态改为false,开始压缩下一个
    private func compressionTask() {
        // 如果当前没有被压缩的图片,获取任务队列的第一张图片,否则退出
        guard !isCompressing, let task = taskQueue.first else { return }
        
        // 设置压缩状态为 true
        isCompressing = true
        DispatchQueue.main.async {
            // 修改当前图片为压缩中
            task.compressionState = .compressing
        }

        compressImage(task) { success in
            DispatchQueue.main.async {
                task.compressionState = success ? .completed : .failed
                self.taskQueue.removeFirst()
                self.isCompressing = false
                self.compressionTask() // 继续下一个
            }
        }
    }
}

3、用户添加图片时只加入队列,不直接压缩

在 UI 上传图片时:

let newImage = ImageItem(url: fileURL, compressionState: .pending)
CompressionManager.shared.enqueue(newImage)

4、图片压缩方法

func compressImage(_ image: CustomImages, completion: @escaping (Bool) -> Void) {
	// 如果压缩成功,返回 true
if success {
        completion(true)
} else {
    completion(false)
    }
}

总结

实现图片压缩可以使用串行任务。

创建管理图片的单例模型,每次新增新的图片,都会追加到单例模型的图片数组中,并调用压缩图片的方法。

压缩图片的方法会判断当前有无被压缩的图片,如果有则return,防止重复压缩。

如果当前没有压缩的图片,就尝试获取单例模型图片数组的第一个对象,如果有该对话的话,然后标记当前的状态为在压缩,并更新图片对象的压缩状态。

当图片对象被压缩后,通过回调闭包,更新图片对象的压缩状态,移除图片数组中已经被压缩的第一个对象,开始压缩下一个对象。

通过压缩图片模型的isCompressing变量,实现真正的串行任务。

此外,使用任务队列时,因为需要修改很多参数,可能会遇到SwiftUI无法跟踪数组内元素属性变化,而不同步SwiftUI的问题,具体请见《SwiftUI无法追踪数组中对象的内部属性变化问题》。

相关文章

1、Swift基于实际案例的异步任务调度队列:https://fangjunyu.com/2024/12/08/swift%e5%9f%ba%e4%ba%8e%e5%ae%9e%e9%99%85%e6%a1%88%e4%be%8b%e7%9a%84%e5%bc%82%e6%ad%a5%e4%bb%bb%e5%8a%a1%e8%b0%83%e5%ba%a6%e9%98%9f%e5%88%97/

2、SwiftUI无法追踪数组中对象的内部属性变化问题:https://fangjunyu.com/2025/07/12/swiftui%e6%97%a0%e6%b3%95%e8%bf%bd%e8%b8%aa%e6%95%b0%e7%bb%84%e4%b8%ad%e5%af%b9%e8%b1%a1%e7%9a%84%e5%86%85%e9%83%a8%e5%b1%9e%e6%80%a7%e5%8f%98%e5%8c%96%e9%97%ae%e9%a2%98/

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注