Swift使用后台线程实际案例
Swift使用后台线程实际案例

Swift使用后台线程实际案例

问题描述

在使用Zip库压缩文件时,发现压缩并下载的文件比较大时(100MB以上),应用就会卡住,鼠标箭头呈现彩虹轮盘的样式。

我想要实现的效果是,Zip库在解压时,可以通过回调闭包的返回进度:

try Zip.zipFiles(paths: ImagesURL, zipFilePath: destinationURL, password: nil) { progress in
    print("进度:\(progress)") // 0.1 0.22 0.4 0.7 0.9 1.0
}

例如,每秒钟返回当前的解压缩进度,根据进度显示解码的动画。

因此,我尝试使用await/async包裹zipImages方法,让压缩和下载变成异步执行。

func zipImages() async {
    do {
        print("zipImages 任务中是否是主线程?", Thread.isMainThread)
        let downloadsDirectory = FileManager.default.urls(for:.downloadsDirectory, in: .userDomainMask)[0]
        let destinationURL = downloadsDirectory.appendingPathComponent("ImageSlim.zip")
        var ImagesURL:[URL] {
            let urls = appStorage.images.compactMap { $0.outputURL }
            return urls
        }
        await MainActor.run {
            showDownloadsProgress = true
        }
        try Zip.zipFiles(paths: ImagesURL, zipFilePath: destinationURL, password: nil) { progress in
            print("zipFiles 任务中是否是主线程?", Thread.isMainThread)
            self.progress = progress
            if progress == 1 {
                showDownloadsProgress = false
            }
        }
        print("打包完成")
    } catch {
        print("打包失败")
    }
}

虽然使用async/await,但是仍然存在压缩和下载大文件时,应用卡住,鼠标指针呈现彩虹轮盘。

在添加输出语句Thread.isMainThread,判断是否为主线程时,发现压缩仍然仍然是在主线程调用的:

zipImages 任务中是否是主线程? true
zipFiles 任务中是否是主线程? true

async/await并没有实现异步调用,这表明Zip本身并不支持async闭包,当压缩和下载时就会阻塞当前线程,从而导致卡住的场景。

解决方案

我们需要在后台线程执行zip操作,当进度更新时再切换回主线程:

func zipImages() async {
    showDownloadsProgress = true    // 显示进度条
    progress = 0.0  // 重置压缩进度条的数值
    DispatchQueue.global(qos: .userInitiated).async {   // 使用后台线程调用
        do {
            print("zipImages 任务中是否是主线程?", Thread.isMainThread)
            let downloadsDirectory = FileManager.default.urls(for:.downloadsDirectory, in: .userDomainMask)[0]
            let destinationURL = downloadsDirectory.appendingPathComponent("ImageSlim.zip")
            var ImagesURL:[URL] {
                let urls = appStorage.images.compactMap { $0.outputURL }
                return urls
            }
            try Zip.zipFiles(paths: ImagesURL, zipFilePath: destinationURL, password: nil) { progress in
                DispatchQueue.main.async {  // 切换回主线程,更新进度条的进度。
                    self.progress = progress
                    if progress == 1 {
                        showDownloadsProgress = false
                    }
                }
            }
            DispatchQueue.main.async {  // 切换回主线程,输出信息
                print("打包完成")
            }
        } catch {
            DispatchQueue.main.async {  // 切换回主线程,隐藏进度条
                self.showDownloadsProgress = false
                print("打包失败")
            }
        }
    }
}

这里主要知识点有两个:

1、使用后台线程处理Zip库的压缩任务:

DispatchQueue.global(qos: .userInitiated).async { Zip库代码 }

2、需要更新进度条UI时,切换回主线程更新:

DispatchQueue.main.async {  // 切换回主线程,隐藏进度条
    self.showDownloadsProgress = false
    print("打包失败")
}

这样就可以实现后台线程处理复杂的工作任务,又可以避免UI卡顿的问题。

总结

因为需要执行比较复杂、耗时的任务,因此需要切换到后台线程执行,否则这些复杂的任务就会占用主线程,从而导致UI卡顿。

如果主线程去处理复杂、耗时的任务,势必会导致UI的卡顿。

因此,后台线程就是用于处理这一场景。

最后,说明一下为什么使用DispatchQueue.global(qos: .userInitiated).async处理后台线程的任务,而不是使用其他的线程模型。

因为全局线程是由系统提供的线程池,用于处理后台任务。全局线程根据任务优先级分为以下六种:

1、.userInteractive: 优先级最高,适用于需要立即完成的短任务(例如动画或用户交互)。

2、.userInitiated: 高优先级,适用于用户主动触发的短期任务,例如用户点击按钮后需要立即执行的任务。

3、.default:默认优先级,适用于没有明确优先级的任务。

4、.utility: 中低优先级,适用于需要耗时较长但对性能要求不高的任务,例如下载文件或处理数据。

5、.background: 优先级最低,适用于后台任务(例如文件预处理)。

6、.unspecified:不指定优先级,系统会决定使用的服务质量。

这里的压缩场景适合.userInitiated,表示拥护期待尽快完成的任务,例如打包ZIP、保存文件、网络加载等。

如果是普通的后台任务,可以使用.default,下载文件、导入大量数据等复杂任务,可以使用 .utility,因此可以根据优先级设置对应的全局线程。

详细内容可以看一下《Swift中的线程模型》文章的全局线程部分。

相关文章

1、Swift管理多线程任务框架GCD:https://fangjunyu.com/2024/11/02/swift%e7%ae%a1%e7%90%86%e5%a4%9a%e7%ba%bf%e7%a8%8b%e4%bb%bb%e5%8a%a1%e6%a1%86%e6%9e%b6gcd/

2、Swift中的线程模型:https://fangjunyu.com/2024/12/25/swift%e4%b8%ad%e7%9a%84%e7%ba%bf%e7%a8%8b%e6%a8%a1%e5%9e%8b/

3、Swift解压ZIP文件:https://fangjunyu.com/2025/04/09/swift%e8%a7%a3%e5%8e%8bzip%e6%96%87%e4%bb%b6/

   

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

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

发表回复

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