弊社エンジニアの記事です。
はじめに
Swift Concurrencyを勉強したので、備忘録を残しておこうと思います
Swift Concurrency
Swift Concurrencyとは、Swift5.5から導入された言語機能で非同期処理と並列処理の実装をサポートします
async/await
関数のreturn矢印の前にasyncをつけることで非同期関数として扱えるようになり、
awaitをつけて呼び出すことで処理が終わるまで実行が一時停止します
以下はコールバック処理で書いた場合とそれをasync/awaitに書き換えた場合の例です
// コールバックで書いたもの
func hello(completion: (String) -> ()) {
sleep(3) // 時間のかかる処理
completion("Hello")
}
func world(completion: (String) -> ()) {
sleep(3) // 時間のかかる処理
completion("World")
}
// 呼び出し
hello { hello in
world { world in
print(hello + world)
}
}// async/awaitで書いたもの
func hello() async -> String {
try? await Task.sleep(until: .now + .seconds(3)) // 時間のかかる処理
return "Hello"
}
func world() async -> String {
try? await Task.sleep(until: .now + .seconds(3)) // 時間のかかる処理
return "World"
}
// 呼び出し
Task {
let hello = await hello()
let world = await world()
print(hello + world)
}比較してみると、コールバックではネストする必要がありますが、async/awaitでは同期的にコードを書けていることが分かります
エラーが発生する非同期関数では、throwsの前にasyncをつけることで非同期関数にします
呼び出し時には、通常と同じようにtryをつけて呼び出します
// エラーが発生する場合
func hello() async throws -> String {
try await Task.sleep(until: .now + .seconds(3)) // 時間のかかる処理
return "Hello"
}
func world() async throws -> String {
try await Task.sleep(until: .now + .seconds(3)) // 時間のかかる処理
return "World"
}
// 呼び出し
Task {
do {
let hello = try await hello()
let world = try await world()
print(hello + world)
} catch {
print("error")
}
}async let
async letを使うことで非同期関数を並列に実行することができます
func hello() async -> String {
try? await Task.sleep(until: .now + .seconds(3)) // 時間のかかる処理
return "Hello"
}
func world() async -> String {
try? await Task.sleep(until: .now + .seconds(3)) // 時間のかかる処理
return "World"
}
// 呼び出し
Task {
async let hello = hello()
async let world = world()
await print(hello + world)
}コールバック関数を非同期関数へ変換
withCheckedContinuationを使うことで既存のコールバック関数を非同期関数へ変換することができます
エラーが発生するコールバック関数の場合は、withCheckedThrowingContinuationを使用します
// コールバック関数を非同期関数に変換
// コールバック関数
func hello(completion: (String) -> ()) {
sleep(3) // 時間のかかる処理
completion("Hello")
}
// 非同期関数に変換
func asyncHello() async -> String {
return await withCheckedContinuation { continuation in
hello { hello in
continuation.resume(returning: hello)
}
}
}
// 呼び出し
Task {
let hello = await asyncHello()
print(hello)
}// エラーが発生するコールバック関数を非同期関数に変換
struct MyError: Error {}
// エラーが発生するコールバック関数
func hello(completion: (Result<String, Error>) -> ()) {
sleep(3) // 時間のかかる処理
let hasError = Bool.random()
if hasError {
completion(.failure(MyError()))
} else {
completion(.success("Hello"))
}
}
// 非同期関数に変換
func asyncHello() async throws -> String {
return try await withCheckedThrowingContinuation { continuation in
hello { result in
continuation.resume(with: result)
}
}
}
// 呼び出し
Task {
do {
let hello = try await asyncHello()
print(hello)
} catch {
print("Error")
}
}Task
タスクは非同期処理の実行単位です
Taskを使うことで同期関数から非同期関数を呼び出すことができるようになります
Task.initは現在の実行環境を引き継ぎます
@MainActorでTask.initを使った場合、タスクは@MainActorで実行されます
// 呼び出し
Task {
let hello = await hello()
print(hello)
}
// @MainActorとすることで明示的にMainActorで実行
Task { @MainActor in
let hello = await hello()
print(hello)
}Task.detachedは現在の実行環境を引き継ぎません
メインスレッドで実行する必要がないものはTask.detachedを使用します
// 呼び出し
Task.detached {
let hello = await hello()
print(hello)
}Taskのインスタンスを保持することでタスクをキャンセルすることができます
以下のコードでは、タスクをキャンセルすることでworld()が実行されずにエラーが発生して終了します
func hello() async throws -> String {
try Task.checkCancellation()
sleep(3)
return "Hello"
}
func world() async throws -> String {
try Task.checkCancellation()
sleep(3)
return "World"
}
// 呼び出し
let task = Task {
do {
let hello = try await hello()
let world = try await world()
print(hello + world)
} catch {
print("error")
}
}
// タスクをキャンセル
task.cancel()TaskGroup
TaskGroupを使うことで並列処理を実装することができます
TaskGroupの作成にはwithTaskGroupを使用します
エラーが発生する場合はwithThrowingTaskGroupを使用します
以下のコードではhello()とworld()が並列に実行され、"HelloWorld"または"WorldHello"で出力されます
func hello() async -> String {
try? await Task.sleep(until: .now + .seconds(3)) // 時間のかかる処理
return "Hello"
}
func world() async -> String {
try? await Task.sleep(until: .now + .seconds(3)) // 時間のかかる処理
return "World"
}
Task {
var printText = ""
await withTaskGroup(of: String.self) { group in
// タスクを作成
group.addTask {
return await hello()
}
// タスクを作成
group.addTask {
return await world()
}
for await text in group {
printText += text
}
}
print(printText)
}タスクのキャンセル
CancellationErrorをスローすることでタスクをキャンセルできます
タスクがキャンセルされている場合、Task.checkCancellationではCancellationErrorがスローされます
また、Task.isCancelledでタスクがキャンセルされているか判定することもできます
func hello() async throws -> String {
// タスクがキャンセルされている場合、エラーをスローして処理を抜ける
try Task.checkCancellation()
sleep(3)
return "Hello"
}
func world() async throws -> String {
// タスクがキャンセルがされているか判定
if Task.isCancelled {
// エラーをスローして処理を抜ける
throw CancellationError()
}
sleep(3)
return "World"
}
// 呼び出し
let task = Task {
do {
let hello = try await hello()
let world = try await world()
print(hello + world)
} catch {
print("error")
}
}
// タスクをキャンセル
task.cancel()actor
actorはデータ競合を防ぐ型でclassと同じ参照型です
プロパティ、メソッド、イニシャライザの定義とプロトコル準拠ができますが、actorの型を継承することはできません
…
記事の続きは下のリンクをクリック!
https://rightcode.co.jp/blog/information-technology/ios-swift-concurrency
【2025年卒】新卒採用エントリー開始しました!
特設ページはこちら:https://rightcode.co.jp/recruit/career-fair-2025
社長と一杯飲みながらお話しませんか?(転職者向け)
特設ページはこちら: https://rightcode.co.jp/gohan-sake-president-talk
もっとワクワクしたいあなたへ
現在、ライトコードでは「WEBエンジニア」「スマホアプリエンジニア」「ゲームエンジニア」、「デザイナー」「WEBディレクター」「エンジニアリングマネージャー」「営業」などを積極採用中です!
有名WEBサービスやアプリの受託開発などの企画、開発案件が目白押しの状況です。
- もっと大きなことに挑戦したい!
- エンジニアとしてもっと成長したい!
- モダンな技術に触れたい!
現状に満足していない方は、まずは、エンジニアとしても第一線を走り続ける弊社代表と気軽にお話してみませんか?
ネット上では、ちょっとユルそうな会社に感じると思いますが(笑)、
実は技術力に定評があり、沢山の実績を残している会社ということをお伝えしたいと思っております。
- ライトコードの魅力を知っていただきたい!
- 社風や文化なども知っていただきたい!
- 技術に対して熱意のある方に入社していただきたい!
一度、【Wantedly内の弊社ページ】や【コーポレートサイト】をのぞいてみてください。
【コーポレートサイト】https://rightcode.co.jp/
【採用募集】https://rightcode.co.jp/recruit(こちらからの応募がスムーズ)
【wantedlyぺージ】https://www.wantedly.com/companies/rightcode