問題の背景:
Swift 5以前では、associated typeを持つプロトコルに準拠した値を返す際に制約が多く、型をreturn typeとして直接使用することができず、type erasureが必要でした。可読性とパフォーマンスを向上させるために、opaque typesが導入されました。これは、someキーワードを使って公開インターフェースの抽象化を記述するための戻り値です。
問題:
プロトコルを通じて返される実際の型を隠し、dynamic dispatchやtype erasureのコストを回避する必要がある場合、例えばコレクション、シーケンス、ビューコンポーネントを返す場合です。
解決策:
Opaque typesを使用することで、プロトコルに準拠した型を返し、その具体的な実装を隠すことができます。コンパイラは実際の型を知っていますが、呼び出し側はその型を知りません。
例:
protocol Shape { func area() -> Double } struct Circle: Shape { var radius: Double func area() -> Double { Double.pi * radius * radius } } func makeCircle() -> some Shape { return Circle(radius: 3) } let s = makeCircle() print(s.area()) // 動作します
主な特徴:
someを介して返されるプロトコルの背後に隠されています。opaque type(some Protocol)とProtocolの戻り値の型の違いは何ですか?
Opaque typeはコンパイル時に具体的な実装を持っています(外部からは隠されています)。Protocolを返すと、dynamic dispatchが働き、associated typeがある場合はtype safetyがありません。
one関数内でsome Protocolを使用して異なる型を返すことはできますか?
いいえ。すべてのreturnは同じ実際の型を返さなければなりません:
func maker(flag: Bool) -> some Shape { if flag { return Circle(radius: 3) } else { return Square(size: 2) // エラー: return typeが一致しません } }
Protocol内のassociatedtypeはsome Protocolを介して使用できますか?
はい。これがopaque typeの主な用途です:
protocol View { associatedtype Body } func makeView() -> some View { /* ... */ }
指定なしのprotocolを返す関数が、associatedtypeを持つメソッドの利用を許さず、複雑なtype erasureが必要で、コードがコンパイルされないか、最適に動作しません。
長所:
短所:
SwiftUIのViewBuilderはsome Viewを使用して詳細を隠し、型の安全性を高め、コンパイル時間とランタイムを改善します。
長所:
短所: