为什么后台线程是每个iOS开发者的痛点?
\n\n想象一下:您正在编写一个从互联网下载照片的应用程序。一切运行完美……直到用户点击“下载”按钮。屏幕卡住,应用程序“变慢”,几秒钟后iOS直接终止了您的进程。熟悉吗?
\n\n问题在于,默认情况下所有操作都在主线程(main thread)上执行。而主线程负责UI渲染——如果您用耗时任务阻塞它,界面将停止响应触摸。解决方案?在Swift中使用后台线程(background thread)。
\n\n在本文中,我们将通过实际示例来学习如何处理后台线程。您将了解GCD、OperationQueue以及Swift 5.5+中新增的async/await。开始吧!
\n\nSwift中的线程(thread)是什么?
\n\n线程是一个轻量级进程,用于执行程序代码。iOS应用程序有一个主线程(main thread)和多个后台线程。主线程负责UI:更新屏幕、处理手势、动画。所有与界面相关的事情必须在主线程上发生。
\n\n后台线程(background threads)用于:
\n- \n
- 从网络加载数据 \n
- 文件操作(读/写) \n
- 复杂计算(图像处理、加密) \n
- 数据库操作 \n
如果您尝试从后台线程更新UI——应用程序会崩溃并报错,或者您会看到界面出现奇怪的行为。
\n\nGCD(Grand Central Dispatch)——经典方式
\n\nGrand Central Dispatch(GCD)是一个内置于iOS/macOS的低级线程管理库。它通过队列(queues)工作:您将任务放入队列,系统自行决定在哪个线程上执行它。
\n\nDispatchQueue.main——主线程
\n如果您需要在主线程上执行代码(例如,更新UILabel),请使用:
\n\nDispatchQueue.main.async {\n self.label.text = "加载完成!"\n}\n\nDispatchQueue.global()——后台线程
\n对于后台任务,请使用全局队列:
\n\nDispatchQueue.global(qos: .background).async {\n // 繁重的工作\n let imageData = try? Data(contentsOf: url)\n \n // 返回主线程更新UI\n DispatchQueue.main.async {\n self.imageView.image = UIImage(data: imageData!)\n }\n}\n\nqos(Quality of Service)参数决定任务的优先级:
- \n
.userInteractive——最高优先级,用于UI \n .userInitiated——用户等待结果 \n .utility——耗时任务(文件下载) \n .background——低优先级(同步、备份) \n
OperationQueue——更多控制
\n\nOperationQueue是GCD之上的封装,提供额外功能:
\n- \n
- 取消任务 \n
- 任务之间的依赖关系 \n
- 最大并发线程数 \n
下载多个图像的示例:
\n\nlet queue = OperationQueue()\nqueue.maxConcurrentOperationCount = 2 // 同时最多2个任务\n\nlet operation1 = BlockOperation {\n // 下载第一个图像\n sleep(2)\n print("照片1已加载")\n}\n\nlet operation2 = BlockOperation {\n // 下载第二个图像\n sleep(3)\n print("照片2已加载")\n}\n\n// operation2仅在operation1完成后开始\noperation2.addDependency(operation1)\n\nqueue.addOperations([operation1, operation2], waitUntilFinished: false)\n\n当需要按严格顺序执行任务或限制资源时,这非常方便。
\n\n现代async/await(Swift 5.5+)
\n\n从Swift 5.5开始,Ap