【物語で学ぶ Swift Concurrency】Actor と Objective-C 連携方法

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` に準拠しているとみなされるようになるんだ。

```swift
@objc actor MyActor {
    // ...
}
```

こう書くと、この `MyActor` は Objective-C から使えるようになる。

**アカリ：**  
`NSObjectProtocol` にも自動で準拠するんですね。ただ、Swift のアクターが持っている「同期メソッド」「非同期メソッド」みたいなのはどうなるんですか？

---

### 2. 非同期メソッド (`async`) と 同期メソッド

**カナメ先生：**  
実はこれがポイントなんだ。Objective-C では、**アクターアイソレーション** という概念を直接理解する仕組みがない。だから Swift のアクターで **同期メソッド** を作っても、Objective-C 側から安全に呼び出す手段がないんだよ。結果的に、

- **非同期メソッド (`async`)** は `@objc` で公開できる  
- **同期メソッド** はアクターのアイソレーション内でしか使えないので、ふつうに `@objc` では公開できない

というルールが生まれる。

**アカリ：**  
同期メソッドは、アクターの“保護された領域”内に閉じている、というイメージですね。

**カナメ先生：**  
そうそう。だからアクターの同期メソッドをそのまま `@objc` にしようとすると、コンパイルエラーになってしまう。

例としては、こんなイメージだね：

```swift
@objc actor MyActor {
    @objc func synchronousMethod() {
        // error: part of actor's isolation domain
        // 同期メソッドはアクター内部だけで使うものなので
        // Objective-C に公開できない
    }
}
```

これはエラーになるんだ.

---

### 3. 非同期メソッド (`async`) の `@objc` 公開

**アカリ：**  
じゃあ、非同期メソッドなら大丈夫なんですね？

**カナメ先生：**  
そうだよ。非同期メソッドは、アクター内部の状態をアクセスする際に安全に扱えるし、Objective-C 側で呼び出すときは **コールバック** みたいな仕組みに自動的に変換されるんだ。こんな感じ：

```swift
@objc actor MyActor {
    @objc func asynchronousMethod() async {
        // ここはアクターアイソレーションで守られているけど、
        // Objective-C から呼べるようになる（内部的にはコールバック）
    }
}
```

この `@objc func asynchronousMethod() async { ... }` はコンパイルエラーにはならないんだ。Objective-C から呼び出す際には「完了ハンドラー」を使う形に自動変換してくれるイメージだね。

---

### 4. `nonisolated` を使うことで同期メソッドを公開

**アカリ：**  
同期メソッドをまったく公開できないとなると不便そうです。何か方法はありますか？

**カナメ先生：**  
あるよ。どうしても同期メソッドを Objective-C で呼びたい場合は、**`nonisolated`** 属性を使う手がある。これは「このメソッドはアクターの保護ドメインには属していない」という宣言になるんだ。つまり、アクターアイソレーションから外れるから Objective-C にも公開できる。

```swift
@objc actor MyActor {
    @objc nonisolated func nonIsolatedMethod() {
        // アクターアイソレーションを使わないコード
        // => Objective-C からも呼べる
    }
}
```

こう書くと同期メソッドでもコンパイルエラーにはならなくなる。ただし、その代わりに**アクター内部の安全性（アイソレーション）を活かせなくなる**ので、使う側が自分でスレッドセーフか気をつける必要があるんだよ。

**アカリ：**  
なるほど、`nonisolated` はアクターを通じた並行安全性の恩恵を受けられない分、Objective-C からアクセスできるようになるわけですね。

---

### 5. Objective-C との相互運用時の注意

**アカリ：**  
結局、Objective-C はアクターアイソレーションがわからないからこそ制限がある、ということですか。

**カナメ先生：**  
その通り。Objective-C から見ると、アクターが持つ「同期メソッドなのか」「非同期メソッドなのか」といった概念を直接は把握できない。だから、

1. **非同期メソッド (`async`)** はそのまま `@objc` として公開できる。  
2. **同期メソッド** はアクターのアイソレーションと深く関係するので、通常は `@objc` として公開できない。  
3. もし同期メソッドを公開したいなら `nonisolated` を付けて「アクターアイソレーション外ですよ」と明示的にする必要がある。

という具合に整理されているんだ.

---

### まとめ

- **アクターを `@objc` で宣言** することで、Objective-C からそのアクターを利用できるようになる。  
- **非同期メソッド (`async`)** は `@objc` として公開可能。  
- **同期メソッド** はアクターアイソレーションの中でしか使えないため、通常 `@objc` として公開できない。  
- 同期メソッドを公開したい場合は **`@objc nonisolated`** を使う。ただし、その分アクターアイソレーションの恩恵は受けられなくなる。  
- **Objective-C はアクターアイソレーションを理解しない** ので、制限事項や安全性に注意が必要.

**アカリ：**  
わかりました。つまり、アクターを Objective-C と連携させるときは「非同期メソッドだけは問題なく公開できる」「もし同期メソッドを公開したいなら `nonisolated` を付ける」という方針なんですね.

**カナメ先生：**  
その通り！ Swift のアクターと Objective-C の連携をするときは、このルールを意識しておけばだいたい困らないはずだよ。並行処理を使うアプリを作るときにぜひ活用してみてね.

**アカリ：**  
ありがとうございます！頑張ってみます!

## 参考
[Actor interoperability with Objective-C](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0306-actors.md#actor-interoperability-with-objective-c)