1. APIとは?
- API = 外から見える「メニュー表」や「窓口」。
- 「ホットコーヒーをください」と注文できるのは、カフェがそういう窓口を公開しているから。
- どう作っているか(豆の種類・器具)は本来隠すべき「内部実装」。
👉 「APIで内部実装を過剰に公開」とは、
「メニュー表に『ネスカフェ赤瓶をスプーン2杯』と書いてしまうこと」。
→ 後から豆を変えたらメニューと実態がズレて APIが壊れる。
2. Tupleとは?
- Tuple = 小さな袋にまとめたデータ。
- 例:
("太郎", 25)= 名前と年齢が1つの袋に。 - 軽量なデータのまとめ方。
3. なぜ some が必要なのか?
(A) 型を具体的に返す場合
func makeAnimal() -> Cat { Cat() }
👉 これは「この関数は絶対にネコを返します」と内部実装まで公開している状態。
→ もし将来 Dog に変えたいと思ったら API を変えざるを得ない。柔軟性ゼロ。
(B) プロトコル型(Existential, any)を返す場合
func makeAnimal() -> any Animal { Cat() }
👉 これなら「Animal を返す」と言えるので実装は隠せる。
ただし Swift では Existential Container という箱に詰め込む仕組みが働く。
Catは本来1バイトで済むDogは8バイト必要- でも
any Animalという箱に入れると両方に対応するため 40バイト確保される
つまり
- 値を渡すとき → 箱に詰める処理(オーバーヘッド)
- メソッドを呼ぶとき → 箱を開いて間接呼び出し(オーバーヘッド)
👉 柔軟だが遅い。
(C) some を返す場合(Opaque Result Type)
func makeAnimal() -> some Animal { Cat() }
- 「Animal を返す」としか外には見えない
- でも内部的には
Catに固定されているので 最適化できて速い - 内部を隠しつつ、
-> Catと同等のパフォーマンス
👉 柔軟かつ速い。
4. ジェネリクスと「リバースジェネリクス」
ジェネリクス(普通の <T>)
func useAnimal<A: Animal>(_ animal: A) {
animal.foo()
}
- 呼び出し側が「A = Cat」と決める。
- 利用者が型を選ぶ仕組み。
リバースジェネリクス(逆の発想)
func makeAnimal() -> <A: Animal> A {
return Cat()
}
- 実装者が「A = Cat」と決めて、利用者には「Animal として扱えるよ」とだけ見せる。
- これを簡単に書けるシンタックスシュガーが
-> some Animal。
👉 「some = リバースジェネリクスの糖衣構文」というのはこのこと。
5. SwiftUI と some View の関係
var body: some View {
VStack {
Text("Hello")
Button("Tap") { }
}
}
- 本当の型は
VStack<TupleView<(Text, Button<Text>)>>のように爆長い。 - それを API として公開すると 内部構造をバラすことになるし、実装を少し変えただけで壊れる。
some Viewとすることで「View を返す」だけを約束できる。
👉 内部を隠して安全に保ちつつ、速い。
6. まとめ(全部の比較)
-> Cat
👉 速いけど中身を公開しすぎ(柔軟性なし)-> any Animal
👉 柔軟だが遅い(Existential Containerのオーバーヘッド)-> some Animal
👉 内部は隠しつつ速い(リバースジェネリクスの糖衣構文)
📝 結論
- API = メニュー表
- Tuple = 小さな袋
- 内部実装の過剰公開 = メニューに「豆の種類」まで書くこと
some= 実装者が型を決めて隠す(柔軟 + 高速)any= 利用者がどの型でも扱える(柔軟だけど遅い)- SwiftUI の
some Viewは、この「隠すけど速い」メリットを最大限に使っている。

コメント