iOS15之Swift5.5 Concurrency 并发编程初探

1,273 阅读2分钟

前言

在Swift5.5以前或Object-C时代iOS开发要实现并发代码,一般要自己使用多线程,如DispatchGroup,DispatchSemaphore等等,且都是命令式代码,并不能使用诸如await等响应式代码的方式,异步转同步直观的获取异步资源。Swift5.5 Concurrency就解决了这个问题,我认为这是一个非常棒的设计,虽然其他语言老早就有类似的语法,但不得不再赞一次。但是令人遗憾的是,Swift5.5 Concurrency编程是建立在Xcode13以及iOS15之上的,也就是说旧项目的支持依然无法使用。以下是介绍Swift5.5 Concurrency的写法,我也会使用旧有的线程方式作为对比。

参考文献

Swift5.5 Concurrency

demo code

基本语法

这里我以下载网络图为例子。

首先要说的是 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

}