Swift で登場した Actor 機能は、データ競合を避けるために設計された並行処理用の参照型です。ところが、この Actor を Objective-C と連携させたい場合、少し特殊なルールを理解しておく必要があります。本記事では、高校生アカリとカナメ先生の対話形式で、Actor と Objective-C をどう扱うのかを解説していきます。
【登場人物】
- アカリ:高校生。プログラミング初心者だが Swift と Cocoa/Objective-C を学び始めたところ。
- カナメ先生:高校の情報科教師。Swift の並行プログラミングやアクターについて詳しい。
アクターと Objective-C の相互運用について
アカリ:
先生、この前 Swift のアクターっていうのを勉強したんですけど、Objective-C とどうやって連携できるのか全然わかりません。学校の Mac で Cocoa アプリを作りたいなと思ってるんですが……。
カナメ先生:
なるほど、Swift のアクターと Objective-C の連携は少しややこしいかもしれないね。まず、アクターは並行処理でデータが競合しないように作られた特殊な“参照型”なんだ。アクターアイソレーション と呼ばれる仕組みで、アクター内部のプロパティやメソッドを安全に守ってくれるんだよ。
アカリ:
アクターアイソレーション……。つまりアクターの中では同時に複数の処理が走ったりしない、という感じでしょうか?
カナメ先生:
大まかにはそうだね。アクターは自分の状態を守るために“1度に1つのタスクしか実行しない”というルールで動いている。さて、これを Objective-C と連携させたいとき、@objc
属性を使うことができるんだ。
1. @objc
とアクター
カナメ先生:
Swift の型に @objc
を付けると Objective-C からもその型を見たり使ったりできるようになるよ。アクターも同じように、頭に @objc
と書くことで、NSObjectProtocol
に準拠しているとみなされるようになるんだ。
@objc actor MyActor {
// ...
}
こう書くと、この MyActor
は Objective-C から使えるようになる。
アカリ:
NSObjectProtocol
にも自動で準拠するんですね。ただ、Swift のアクターが持っている「同期メソッド」「非同期メソッド」みたいなのはどうなるんですか?
2. 非同期メソッド (async
) と 同期メソッド
カナメ先生:
実はこれがポイントなんだ。Objective-C では、アクターアイソレーション という概念を直接理解する仕組みがない。だから Swift のアクターで 同期メソッド を作っても、Objective-C 側から安全に呼び出す手段がないんだよ。結果的に、
-
非同期メソッド (
async
) は@objc
で公開できる -
同期メソッド はアクターのアイソレーション内でしか使えないので、ふつうに
@objc
では公開できない
というルールが生まれる。
アカリ:
同期メソッドは、アクターの“保護された領域”内に閉じている、というイメージですね。
カナメ先生:
そうそう。だからアクターの同期メソッドをそのまま @objc
にしようとすると、コンパイルエラーになってしまう。
例としては、こんなイメージだね:
@objc actor MyActor {
@objc func synchronousMethod() {
// error: part of actor's isolation domain
// 同期メソッドはアクター内部だけで使うものなので
// Objective-C に公開できない
}
}
これはエラーになるんだ.
3. 非同期メソッド (async
) の @objc
公開
アカリ:
じゃあ、非同期メソッドなら大丈夫なんですね?
カナメ先生:
そうだよ。非同期メソッドは、アクター内部の状態をアクセスする際に安全に扱えるし、Objective-C 側で呼び出すときは コールバック みたいな仕組みに自動的に変換されるんだ。こんな感じ:
@objc actor MyActor {
@objc func asynchronousMethod() async {
// ここはアクターアイソレーションで守られているけど、
// Objective-C から呼べるようになる(内部的にはコールバック)
}
}
この @objc func asynchronousMethod() async { ... }
はコンパイルエラーにはならないんだ。Objective-C から呼び出す際には「完了ハンドラー」を使う形に自動変換してくれるイメージだね。
4. nonisolated
を使うことで同期メソッドを公開
アカリ:
同期メソッドをまったく公開できないとなると不便そうです。何か方法はありますか?
カナメ先生:
あるよ。どうしても同期メソッドを Objective-C で呼びたい場合は、nonisolated
属性を使う手がある。これは「このメソッドはアクターの保護ドメインには属していない」という宣言になるんだ。つまり、アクターアイソレーションから外れるから Objective-C にも公開できる。
@objc actor MyActor {
@objc nonisolated func nonIsolatedMethod() {
// アクターアイソレーションを使わないコード
// => Objective-C からも呼べる
}
}
こう書くと同期メソッドでもコンパイルエラーにはならなくなる。ただし、その代わりにアクター内部の安全性(アイソレーション)を活かせなくなるので、使う側が自分でスレッドセーフか気をつける必要があるんだよ。
アカリ:
なるほど、nonisolated
はアクターを通じた並行安全性の恩恵を受けられない分、Objective-C からアクセスできるようになるわけですね。
5. Objective-C との相互運用時の注意
アカリ:
結局、Objective-C はアクターアイソレーションがわからないからこそ制限がある、ということですか。
カナメ先生:
その通り。Objective-C から見ると、アクターが持つ「同期メソッドなのか」「非同期メソッドなのか」といった概念を直接は把握できない。だから、
-
非同期メソッド (
async
) はそのまま@objc
として公開できる。 -
同期メソッド はアクターのアイソレーションと深く関係するので、通常は
@objc
として公開できない。 - もし同期メソッドを公開したいなら
nonisolated
を付けて「アクターアイソレーション外ですよ」と明示的にする必要がある。
という具合に整理されているんだ.
まとめ
-
アクターを
@objc
で宣言 することで、Objective-C からそのアクターを利用できるようになる。 -
非同期メソッド (
async
) は@objc
として公開可能。 -
同期メソッド はアクターアイソレーションの中でしか使えないため、通常
@objc
として公開できない。 - 同期メソッドを公開したい場合は
@objc nonisolated
を使う。ただし、その分アクターアイソレーションの恩恵は受けられなくなる。 - Objective-C はアクターアイソレーションを理解しない ので、制限事項や安全性に注意が必要.
アカリ:
わかりました。つまり、アクターを Objective-C と連携させるときは「非同期メソッドだけは問題なく公開できる」「もし同期メソッドを公開したいなら nonisolated
を付ける」という方針なんですね.
カナメ先生:
その通り! Swift のアクターと Objective-C の連携をするときは、このルールを意識しておけばだいたい困らないはずだよ。並行処理を使うアプリを作るときにぜひ活用してみてね.
アカリ:
ありがとうございます!頑張ってみます!