はじめに
Swift Concurrency使ってますか。非同期処理がスッキリ記述できて良いですよね。
そんな中なんとなくの理解でActorを使っていたのですが、この度理解を深められましたので、私が理解できていなかった部分ベースで解説したいと思います。
Actorって何?
Swift Concurrencyの一部で排他的アクセスを保証するクラスです。
インスタンス化した際に特定のスレッドにバインドされ、そのスレッド以外からはアクセスできませんが、実行された関数は1つずつ順番に実行され排他制御されます。
classとactorの違いとは
- class
- インスタンスはどのスレッドからでもアクセス可能
- 並列処理時にはロックなどで排他制御が必要
- 継承可能
- actor
- インスタンスは特定のスレッドにバインドされる
- 内部のプロパティ、メソッドは排他アクセスが保証される
- アクター間ではスレッドセーフが求められる
- 継承不可だが、プロトコル準拠は可能
つまり従来は自前で対応が必要だった排他制御はactorを使えば簡単に実現できます。
ただactorはデメリットもあり、完全に排他制御を行うためパフォーマンスが落ちる可能性もあります。
isolatedとnonisolatedって何?
actorのインスタンスメソッドに付けられる属性です。これはメソッドが並列処理の制御をどう扱うか決定します。
- isolated
- isolatedメソッドは、メソッド呼び出しごとに1つずつ処理が行われ、前の処理が完了するまで次の処理は実行されません。指定しない場合はこちらになります。
- nonisolated
- このメソッドには並列処理の制御が行われません。つまり同時に複数の呼び出しが実行可能です。nonisolatedメソッドはスレッドセーフにはなりません。
プロトコルを使ったclassでisolatedの警告が出てしまう理由
以下のようにプロトコルに準拠させつつ、@MainActorを定義している場合以下の警告が出ます。
Main actor-isolated instance method 'didSearchTextChange' cannot be used to satisfy nonisolated protocol requirement
protocol SearchUsersPresenterInput {
func didSearchTextChange(_ text: String)
}
@MainActor
class SearchUsersPresenter: SearchUsersPresenterInput {
func didSearchTextChange(_ text: String) {
省略
}
}
理由としてはactorの特性で継承不可だが、プロトコル準拠は可能
というものがありましたが、その都合でプロトコルも記載していないメソッドはisolatedになるわけです。
それをMainActorなclassで実装したので、スレッドセーフには使えませんよとなるわけです。
そのためクラスに準拠させるならプロトコル側でnonisolatedをつける必要があります。クラス側にも付けられますがactorで使わないならプロトコルにつけましょう。
protocol SearchUsersPresenterInput {
nonisolated func didSearchTextChange(_ text: String)
}
最後に
何かしら役に立つものはあったでしょうか。
なんとなくでも動いてしまうのですが、頻繁に使うようなこういった技術はちゃんと理解して使うべきですね。読んでいただきありがとうございました。