前言
在Swift5.5以前或Object-C时代iOS开发要实现并发代码,一般要自己使用多线程,如DispatchGroup,DispatchSemaphore等等,且都是命令式代码,并不能使用诸如await等响应式代码的方式,异步转同步直观的获取异步资源。Swift5.5 Concurrency就解决了这个问题,我认为这是一个非常棒的设计,虽然其他语言老早就有类似的语法,但不得不再赞一次。但是令人遗憾的是,Swift5.5 Concurrency编程是建立在Xcode13以及iOS15之上的,也就是说旧项目的支持依然无法使用。以下是介绍Swift5.5 Concurrency的写法,我也会使用旧有的线程方式作为对比。
参考文献
基本语法
这里我以下载网络图为例子。
首先要说的是 await 必须与 async 搭配使用
func downloadImage(url: String) async -> UIImage? {
return await loadImage(url: url)
}
如果在viewDidLoad 等不能 async的场景怎么办?(无需纠结为什么不行,直接编译会报错,个人理解是Concurrency也是多线程的方式实现,viewDidLoad等方法直接操作的UI必然在主线程,肯定是不能async的)
那就要用Task包裹,如在viewDidLoad获取一张网络图并显示。
使用await可以这么写
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.imageView)
Task {
let image = await self.loadImage()
DispatchQueue.main.async {
self.imageView.image = image
}
}
}
旧的写法可能是:
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.imageView)
self.asyncLoadImage(url: "http://image.jerryfans.com/iterm2_bg_image.jpg") { [weak self] image in
guard let self = self else { return }
guard let img = image else { return }
DispatchQueue.main.async {
self.imageView.image = img
}
}
}
当我们想下载多张网图,且等全部图片下载完再做事情是这样的:
Concurrency方式:
//方式1
func multiImageLoad() async -> [UIImage]? {
var results: [UIImage] = []
await withTaskGroup(of: UIImage.self) { taskGroup in
for ele in origins.enumerated() {
taskGroup.addTask {
return await self.loadImage(url: origins[ele.offset])!
}
}
for await result in taskGroup {
results.append(result)
}
}
return results
}
//方式2
func multiImageLoad() async -> [UIImage]? {
var results: [UIImage] = []
for ele in origins.enumerated() {
results.append(await self.loadImage(url: origins[ele.offset])!)
}
return results
}
然后使用一般是这样:
Task {
guard let images = await multiImageLoad() else { return }
DispatchQueue.main.async {
print(images)
//do your things
}
}
旧方式之一:
func multiImageLoad_old() {
let group = DispatchGroup()
group.enter()
// load img 1
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
group.leave()
}
group.enter()
// load img 2
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
group.leave()
}
group.notify(queue: .main) {
//do your things
}
}
其他语法
如等待两三个接口完成,再下一个接口
func multiWaitFinishLoad() async -> [UIImage]? {
var results: [UIImage] = []
await withTaskGroup(of: UIImage.self) { taskGroup in
for ele in origins.enumerated() {
taskGroup.addTask {
print("begin ele \(ele.offset)")
return await self.loadImage(url: origins[ele.offset])!
}
}
for await result in taskGroup {
results.append(result)
}
//等待上面执行完,再做下面的事情
await taskGroup.waitForAll()
//取消全部
// taskGroup.cancelAll()
print("wait finished and do")
results.append(await loadImage()!)
}
return results
}