ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
最新のSwift APIのデザイン
すべてのプログラミング言語には、ユーザーが期待するいくつかの慣習があります。このセッションでは、Swift APIのデザインで一般的なパターンを、SwiftUI、Combine、RealityKitといった新しいAPIからの例を使用して紹介します。チームの一員としてAppを開発する場合も、他の人が使用できるようにライブラリを公開する場合でも、APIを明瞭かつ正しく使用できるよう、Swiftの新機能の使い方についてご確認ください。
リソース
関連ビデオ
WWDC21
WWDC19
-
ダウンロード
(音楽)
(拍手) こんにちは 私 ベンと同僚のダグが Swiftにおける APIのデザインについて話します バイナリとモジュール両方の 安定性向上と Swiftを存分に使うフレームワークを 紹介できます AppleのSDKの一部として 使いやすい優れたAPIを提供します APIのデザインについては 既知の情報もあると思いますが まず基本的なコンセプトを確認し それがデザインに及ぼす影響を 理解します Swift 5.1の新しい特徴にも 触れる予定です APIの表現をより豊かにできます さらにSwiftUIやRealityKitを含む― 最新のSwiftのフレームワークも お見せします
以前 APIデザインについて 話しました 2016年にSwift APIデザイン ガイドラインを導入した時です SwiftのWebサイトで ご覧いただけます APIに名前を付けたり ドキュメントを書いたりするのに 役立つ助言も含みます 繰り返すつもりはありませんが メッセージがあるとすれば こういうことです 使用する際の明瞭さが APIデザイナーとして 最も大切な目標ということです APIを使ってコードを読む時 それが何をしているか 明らかにしたいですよね またAPIがラクに 正しく使われるのを望むでしょう それには 命名と読みやすさが重要です
命名について前向きな 新しいことが1つあります SwiftだけのAPIにおいては プレフィックスを使用しません これがAPIを より明確にし 読みやすくする助けとなります
CやObjective-Cでは プレフィックスを使いました シンボルはすべて グローバル名前空間にあり 曖昧さを排せなかったからです だからAppleと開発者は 厳しいプレフィックスの規約に 固執しました 一貫性を保つため Objective-C版のあるAPIでは Swift版でも プレフィックスを使用し続けます
しかしSwiftのモジュールシステムは 曖昧さを許容します 型の前にモジュール名を付けるのです 標準ライブラリに プレフィックスはありません Swiftフレームワークからも省けると お気づきですね
ただし注意も必要なので 覚えておいてください
一般的な名前は矛盾がある時 手動で曖昧さを なくすことになります “使用時の明瞭さ”をお忘れなく 特定のフレームワークからの 一般的な名前は コンテクストから見ると 紛らわしいです
この後のトピックは 値型と参照型 プロトコルとジェネリクス 新しい特徴の Key path member lookupと property wrapperも紹介します では値型と参照型からです 簡単に まとめると Swiftは型を作る 3つの基本コンセプトを持ちます クラスと構造体と列挙型です クラスは参照型で 変数を扱う時― 値を持つオブジェクトを参照します コピーすれば その参照内容をコピーしています そのため 参照内容により値を変える場合― 両方の変数が参照する 同じオブジェクトを変更します すると両方が変わります
構造体と列挙型は値型です コピーする時は 全体の内容をコピーします 変更をすれば 1つのコピーが変わっています APIで値型を使うことは 使用時の明瞭さに大きな利益があります 毎回 新しく独自のコピーを 得ていれば どこから値が来たかや 誰かが裏でそれを変える リファレンスを持たないか心配無用です 防衛的なコピーを作る必要はありません
ここまでで疑問が生じます コードに応じて どう参照型と値型を使い分けるかです 使用ケースはそれぞれで 厳しいルールはありません しかし一般的に クラスを使うべき 十分な理由がない限り― クラスより構造体を使うようにします クラスの使用をデフォルトにするなら デフォルトを変更して 様子を見てみてください Swiftでクラスが不可欠となるのは― 参照カウントを通し リソースを管理する場合です しかしクラスを構造体の内部に ラップしたい場合があります
それらは何かが記憶され シェアされるなら 役立つコンストラクトです もし型がアイデンティティを持つなら その観念は その値から 切り離されています クラスに意味があり得るという サインです
RealityKitでこの決定をすべき例を 見てみます RealityKitのAPIは こうしたエンティティを扱います エンティティはシーンに現れる オブジェクトを表し RealityKitのエンジンの中に 記憶され アイデンティティを持ちます シーンを操作するため オブジェクトの外観を変えたり 動かしたりする時は エンジンの中で直接 オブジェクトを操作しています 参照型はRealityKit中のオブジェクトを 動かすハンドルの役割です これは参照型の完璧な使い方です ここでの位置や方向のような エンティティの属性は 値型としてモデルされています ではコードの中での見え方です このシーンを ここで作ることにします 最初にマテリアルタイプを作り― 次にその中で2つ箱を作ります そしてシーンの中に固定します するとコードを使い シーンを操作できるので Y軸に沿って小さい箱を動かせます もしくは大きい箱を45度 回転できます 参照型でこういった操作をしながら シーンを操作しています とても直感的な感じですね 次にここでしたいのは 箱の1つを別の色にすることです 例えば箱の色を赤に変えてみます さてAPIのユーザとして 次に何が起こると予想しますか? 作り出された変数を変えたので 両方の箱が変わるのか 後に作られた箱だけが変化するか つまりマテリアルは 参照型なのか 値型なのか どちらも APIのデザインには 合理的なものでしょう 値型を使う利点は コードの最初に マテリアルタイプを作り 作成時と変更時の間に長い時間があり 前に使っていたことを 忘れた場合です 想定しなかったシーンの変更も あるかもしれません ですからRealityKitは マテリアルを値型にするのです しかし参照方式も 他のAPIには意味があります 重要なのは諸要素が 何のために どう動くのか― 簡潔に説明するモデルを APIが持つことです 最も重要なのは― その動きが 偶発的な実装詳細で 起こるものではないことです 使用するケースに基づく 意識的な選択であるべきです 偶発的な実装詳細とは何でしょうか? 1つの例を見てみましょう マテリアルタイプです 値のように働かせたいので 構造体にします これに簡単なプロパティを与えます テクスチャプロパティも与えます これは参照カウントで リソースの管理が必要です そこでクラスを用います 値型のコピーを作る時は 記憶された全プロパティをコピーします しかし参照型のコピーなら その参照をコピーするだけです このマテリアルタイプをコピーすれば 参照のコピーができるのです 2つのタイプは同じテクスチャ オブジェクトをシェアします それでいいかどうかは テクスチャの実装次第です テクスチャが不変なら問題なく― シェアする際には望ましいことです もしテクスチャが 生成後に変更可能な変数型なら 奇妙なものが作成されます 参照としても値としても機能しませんが 構造体のプロパティを変えられます それは変数の1つに影響します テクスチャ参照を通して オブジェクトに変更を加えれば― 両方の変数に影響を及ぼします ユーザを驚かせるかもしれません 参照方式にとらわれていたなら もっと混乱するでしょう
カギとなる区別を見ていきましょう 値型と参照型 構造体とクラス そして値方式と参照方式の 間の区別です 例えば構造体は値型ですが 常に値として 働くわけではありません いくつか例外がある中でも 一般的なのが パブリックなAPIの一部に 変更可能な参照型を含むケースです 値として働くものが必要なら 参照が変わるものかが問題です
これはいつも明白とは限りません non-finalのクラスを 扱っているとしたら 実際に持つのは 変わりやすいサブクラスかもしれません
この問題を避ける技術は たくさんあります 最も簡単なのは 防衛的なコピーを作ることです これで格納型プロパティを プライベートなものにできます 計算型プロパティでも大丈夫です セッターの内部では テクスチャオブジェクトの コピーを作るだけです これがサブクラスの問題を回避します でもテクスチャが変わりやすければ 問題は解決しません ゲッターのプロパティで 変わってしまうからです 別のオプションも考えましょう
参照型を公開しないことです そして望むプロパティだけを オブジェクト上に― 計算型プロパティとして公開します これでプロパティを作り出せます ゲッターの中では 関連するプロパティに進みます セッターでは 最初に そのオブジェクトの― 参照型の値がユニークなものであるか チェックします そうでなければ その時点で 変更前に このテクスチャオブジェクトの 完全なコピーを作ることができます ユニークさのチェックで 1行を加えることで コピーオンライト方式を 実装してきました 同時に参照型に望ましいプロパティを 公開します
次はプロトコルとジェネリクスです APIを使う時 値型が― いかに明瞭であるかを見てきました しかしCGPoint型などもあり 値型は新しいものではありません 違いは何でしょう? Swiftで違うのは クラスと同じように― 構造体や列挙型に プロトコルを適応させられる点です ジェネリクスで異なる型に コードをシェアできます 異なる型の間で コードをシェアするなら 基本的に ベースクラスがシェアされている― クラスヒエラルキーを 作る必要はありません その代わりSwiftでは プロトコルで始まります しかし それはXcodeを開き 空っぽのソースがある時です 最初に“protocol”と タイプすることではありません SwiftのAPIデザインでは 使用例に応じた型で開発を進めます いくつかの型や機能を試しながら シェアしたいコードを理解します ジェネリクスを使い コードを因数分解します 新しいプロトコルを 作るのかもしれません しかし まずは存在する プロトコルから必要なものを作ります デザインする時は 構成可能かを確かめましょう そして1つのプロトコルを 作る代わりに ジェネリック型を作ることを考えます 今 挙げたいくつかを示す 例を見ます 幾何のAPIを作ってみましょう その一部として幾何ベクトル上での 操作を作りたいのです プロトコルから作るかもしれませんね それに定義したい操作を 与えられます ドット積や2つのベクトル間の 距離のようなものです
ベクトルの大きさを記憶するので 幾何ベクトルにSIMDプロトコルを 絞り込ませるかもしれません SIMDタイプを適用すれば― 同じ処理を複数のデータに 同時に実行できるので効率的です Swift 5.1で機能が増え― 幾何計算を行うのには完璧です SIMDタイプに寸法を記憶させ スカラーSIMD上で作動させ 望む計算ができるようにします
こうしてプロトコルを定義できれば ベクトルに関し デフォルト実装を行うことができます それから 型に この新しい機能を追加できるよう― プロトコルの適合性を確認します このように プロトコルの定義― デフォルト実装 適合性の確認 3つの過程があります 少し煩わしいです 振り返ってみるのも大事です “そのプロトコルは必要だったか?” どの適合性も カスタム実装できないという事実は プロトコルが役立っていないという 警告サインです 型ごとのカスタマイズが 行われないのです これは どのSIMDタイプでも同じです ではプロトコルは 何を与えてくれるのか?
もし新しいプロトコルに デフォルト実装を書く代わりに SIMDプロトコルに直接 拡張子として 書き込むなら それで終了です このページのコードでは― SIMDタイプのすべてに 必要とする機能を与えています
プロトコルの精巧な階層を作り出し 異なる型を 分類してみたくなるかもしれません しかし この種のタイプ動物学は 必ずしも必要ではありません
ここには実際的な考察もあります この単純な拡張子に基づいた アプローチは コンパイラには処理するのが簡単です バイナリサイトは 監視テーブルなしなら小さくなります 大きなプロジェクトでは 複雑なプロトコルがたくさんありますが 単純化のアプローチと プロトコルの数を減らすことで アプリケーションのコンパイル時間を 改善できると分かりました
拡張子アプローチは 少人数のヘルパーにはいいものですが より充実したAPIのデザインでは 問題が生じます
プロトコルを作ることについて 幾何ベクトルを定義し ストレージに使うSIMDを 制限させると言いました それで正解ですか? これはIs-a関係ですか? 本当に幾何ベクトル Is-a SIMDタイプと言えますか? ベクトルを足したり引いたり 有用な操作もあるでしょう しかし他はダメで ベクトルを掛けたりはできません あるベクトルに1を足すこともできない 操作はSIMDタイプ上で可能です 他のコンテクストで意味のある 定義もあります 幾何の中だけじゃありません
使いやすいAPIをデザインするなら 他のオプションも考えます それはhas-a関係として― Is-a関係の代わりになるものです ジェネリック構造体の内部に SIMD値をラップするのです それで代わりに幾何ベクトルの 構造体ができました そしてSIMDストレージタイプで ジェネリックにし どんなフロートポイントタイプも 異なった数の寸法も扱えるのです
一度 これを行えば 新しいタイプ上で公開するAPIに きめ細かいコントロールを持てます 2つのベクトルを互いに 加えたものを定義するもので あるベクトルに1つの数を 加えたものではありません またはスケーリングファクターで あるベクトルの掛け算を定義しますが 2つのベクトルを 掛け算したものではありません
さらにジェネリック拡張子も使えます ドットプロダクトの実装と距離は 以前とまったく同じのままです
この技術を標準ライブラリで使いました 例えば1つの SIMDプロトコルを持ちます それからジェネリックで SIMDタイプの違ったサイズを表す 構造体を持ちます
SIMD2または SIMD3プロトコルはありません それらは多くの値を 加えようとはしません ユーザはSIMD3タイプの拡張によって SIMDの特別なサイズに― ジェネリックコードを書けます それは3次元のSIMDタイプ上で 定義したい外積操作を伴います
ジェネリックタイプには プロトコルが実現可能な最大級の 力と伸長性があります さらにプロトコルの機能を使います ジェネリックSIMD上でフローティング ポイントプロトコルを持ち ビルディングブロックを もたらします
さて 幾何ベクトル型上に クロス乗積操作を書けました しかし その実装は 少し見苦しいように見えます X Y Z座標に たどりつくため “value”の格納を続けています これをクリーンアップします X Y Zのためベクトルタイプ上に プロパティを書けました Swift 5.1には Key path member lookupという 新しい機能があります これは複数の計算型プロパティを 公開できるような― 1つのサブスクリプトを書けます 好みで使えばいいものですが 一気に幾何ベクトル上で SIMDでのプロパティを公開するなら これを使うことができるでしょう では方法です まず幾何ベクトルタイプを dynamicMemberLookup属性と タグ付けします そして次に―
コンパイラがdynamicMember サブスクリプトを書くよう促します そしてサブスクリプトが キーパスを取ります サブスクリプトを実行すれば 自動的にキーパス経由で アクセスできるどんなプロパティも 幾何ベクトルタイプ上の プロパティとして公開されるのです ですからキーパスを SIMDストレージタイプに持っていき― スカラーを返したいのです さらにキーパスを使って進み 格納されたvalueから値を回収し 戻すだけです
すると幾何ベクトルはSIMDが持つ 全プロパティを手に入れます 例えばX Y Z軸を手に入れます Xcodeの中の オートコンプリートにも出現します Swift 5のこの特徴を 文字列ベースで試せば このバージョンが完全に 安全なタイプという違いが分かります コンパイル時間で多くを行います
X Y Zプロパティにアクセスできるため クロスプロダクト操作の実装を クリーンアップできます かなり助かります
dynamicMemberは それほど役には立ちません またサブスクリプトに 複雑なロジックを置けます もう1つの例を見ましょう
コピーオンライト方式によって― テクスチャから プロパティを公開する前の例です プロパティは1つでも 同じコードを毎回 書くのは厄介です テクスチャの全プロパティを コピーオンライト方式で 公開するとしたら? dynamicMemberLookupで それが可能です まずdynamicMember属性を加えます
次にサブスクリプト操作を実装し 書き込めるキーパスを 持ってこさせます プロパティの取得と設定の両方を 行いたいからです リターンタイプ上でジェネリックにし テクスチャから すべての型を手に入れるのです
次にゲッターとセッターの実装です ゲッターでは 以前やったように進みます しかしセッターでは変更前に ユニークな参照かのチェックと テクスチャの完全コピーを加えます これで あまり長くない サブスクリプト方式では テクスチャのプロパティすべてを マテリアルタイプは コピーオンライト方式で公開します 型を使って値方式を得る 便利な方法です
新しい特徴は 多くのアプリケーションを持ちます property wrapperという 5.1の新しい特徴と共に 組み立てられます 続いてダグが説明します ダグ (拍手) ありがとう ベン Swiftは明瞭で簡潔なコードのために デザインされました 表現力のあるAPIを作れて コードの再利用もできます ジェネリクスとプロトコルは 機能や型を再利用できるように ジェネリックコードを 作るものです property wrapperは Swift 5.1の新しい機能です 計算型プロパティから 効率的にコードを再利用します ここにある 大きなコードの塊のようなものです では何が起こってるのでしょうか? 試みていたのはパブリックプロパティを 公開することでしたね? ここに それがあります しかしユーザや顧客全員に 値を書いてほしくはないので ポリシーを定めます 計算型プロパティで 我々の実際のストレージは イメージ格納型プロパティの中です ストレージにはゲッターと セッターというゲートがあります
コードが多く 数秒かかりました 分かる方もいるでしょう 変わりやすいのに とても道のりが長いのです
こちらは もっとマシです 1つのラインになったコードで アクセスするための 2つのプロパティの代わりです そして“怠け者”という意味の “lazy”を用いて 方式をお伝えします ドキュメントしやすく 読みやすいのです だからSwift 1以来 “lazy”が言語にあるのです
さて 厄介なのは― これが一般的な問題の 1つの例ということです 別例のコードストラクチャを見ます 基本的には同じです しかしポリシーは ここのゲッターと セッターで実装されていても 異なります 論理を通して見るなら 遅い初期化パターンだと分かるでしょう 読む前に一度 セットするか 初期化しないと失敗します これは一般的なことです この種のコードは多発します この問題に対処する 別の言語がなければ― Swiftを拡張できないでしょう もっと一般的に解決し ライブラリの構築を望んでいます ライブラリでは ポリシーを 分離するという考えを持ちます property wrapperの裏の概念で boilerplateを排除し 表現力のあるAPIを実現します
こんなイメージです あるプロパティを宣言していることを 明確化したいのです ここでは 変化するパブリックテキストと― 後で初期化されるproperty wrapperに 適応することです 特定の方式のセットとポリシーを 与えるためです
後で初期化されるカスタム属性です Swiftでは新しい考えです 本質的には何であろうと 時間のかかる初期化に 適応せよということです しかしコードの観点からすると これは大変“怠けて”います 我々に同じ利益をもたらします boilerplateは取り除きましたが― また これを宣言する時点で 方式を文書化してきました これは煩雑なコードより ずっと読みやすいのです さて 遅い初期化がどんなものか 実際に見てみましょう これは たった今 見たのと同じコードです 遅い初期化の裏にある 同じPolicy Patternです ゲッターとセッターがあり セットはアップデートしています ゲッターはセットしてるかを確かめ 値を返します かなり直接的です 単純なジェネリック型を 面白くしているのは property wrapperです 一番上にproperty wrapper属性だと 示されています カスタム属性構文が指定するものを 他のプロパティに 適応できるようにするものです property wrapper属性と共に 要求が来ます 主要なものは valueプロパティを持つことです これが全ポリシーが 実装されるところです 後で初期化されるプロパティへの アクセスは ここを通ります 我々は遅い初期化が何かという ある抽出された考えを見つけています この例で面白いことは パラメータを取らない初期化子を 宣言することです
property wrapperに伴う オプションです しかし ここで ラッパーに適応するプロパティは 初期化子を通して自由に行くため 暗黙的な初期化を手に入れます
それでは―
実際に使いましょう property wrapperを 特定のプロパティに応用して使う時 コンパイラは そのコードを 2つのプロパティに翻訳します 本質的に最初に見たパターンに 拡張しています 補助メモリがあり $prefixが見えます この型はproperty wrapperの インスタンスです “LateInitialized”と あります ストレージを提供しています パラメータ初期化子を 呼び出さなくても コンパイラによって初期化されます 暗黙的な初期化ができるのです 遅い初期化は自由度が上がります ストレージにnilをセットします 他にも コンパイラは テキストをプロパティに翻訳します ゲッターが作り出すのは $textへのアクセスです 値を求めてゲッターを呼び出すことで $textから値を取り戻します セッターにも同じことをして $textに新しい値を書きます property wrapper型に ストレージが持てるようになります ローカルでもどこでもです ゲッターとセッターの値を通し そのデータへアクセスすることに ポリシーを実行してください
いい感じですね 我々はポリシーを解きほぐし 後で初期化されるラッパーの中で 1つの場所に置きました 好きな型のプロパティで いくつでもでき はるかに簡単で― boilerplateを少なくします
では もう1つの例です ベンは値方式と 参照方式について話しました 参照方式で 変わりやすい状態を扱う場合 どこかの時点で 防衛的なコピーが必要になります 当然 必要なら手動でできます でもproperty wrapperを 作らない理由は? ここでのproperty wrapperは 基本的に以前 見てきたものと同じです ある種のストレージと valueプロパティがあります property wrapperのための ポリシーはセッターの中です 新しいvalueを手に入れる時 コピーしてください コピーにはNSCopyingを使うので コピー方法を呼び出したら キャストするだけです このコピーに関して面白いことは 初期化子としてinitialValueを 供給することです パラメータのない 初期化子のようなものです property wrapperには 不要ですが そこにある時は このproperty wrapperで ラップされる― デフォルト値を供給します デフォルトは初期化子に与えられます 初期化子について望む いかなるポリシーも実施できます 例の場合 セットポリシーと同じです 防衛的なコピーを作り 割り当てることができます
さて―
見てみましょう UIBezierPathのコピーを 定義するとします コンパイラはこれを 2つの違った定義に翻訳します まず初期化子です このパスを作る時は常に UIBezierPathのデフォルトを持ちます 作られた空っぽの例です また複数のプロパティに 展開する時は バックアップの格納型プロパティ $pathがあります では初期化の方法です ユーザが与えた初期値を initialValue初期化子に与えます これで防衛的コピーがされます ゲッターとセッターは同じに見えます ゲットとセット両方に $path.valueです これはデフォルトによる正しい方式です 防衛的コピーがされます この場合 初期値は 新しいオブジェクトを作ります それをコピーしましょう 少し最適化できるのでやりましょう 防衛的コピーの拡張に 魔法は必要なく 型を使います property wrapperのように 振る舞います withoutCopyingの初期化子を与えます
これを使う時は 型に応じた初期化子を書きます ここでは余分なコピーを避けるため― $pathにwithoutCopying初期化子を 呼び出すように割り当てています 魔法のようなことは 起こっていません $pathは格納型プロパティです コンパイラが property wrapperパターンを応用する 一部として生成しただけです 独自の初期化子を セットアップする時を含め いつでも普通の変数のように扱えます
しかしboilerplateが残っています 初期化子を書くのが手間です そこで 他の初期化フォームがあります カスタマー属性を宣言する場合には “DefensiveCopying”で 要求すれば何でも初期化できます バックアップの格納型プロパティを 初期化するため withoutCopyingの初期化子を 呼び出せます これは いい宣言です デフォルトの初期化子を自由に使えます
いいですね つまりproperty wrapperは かなり強力です データにアクセスするための ポリシーという考えを無視し どう格納させるか決定できます データへのアクセスを決定できます ユーザがproperty wrapperと 一緒にするのは カスタム属性構文をシステムに 結び付けるために使うことだけです property wrapperを開発していて データアクセスという考えに関し 多くの異なるユーザたちを 見つけてきました ユーザデフォルトの例を 見たかもしれません ブール型として言及した Swiftのプロパティと 文字で型付けされたものを 関連付けていきます どのようにアクセスするか 構文に記述していきます ユーザデフォルトを 扱うためのロジックはすべて ユーザデフォルト property wrapperの中で終わっています
thread local storageを望むなら スレッド固有を構築します スレッド固有のproperty wrapperを 適用できます スレッド固有のストレージを扱う 詳細はproperty wrapperにあります local memory poolと 思えばいいのです
Swiftのコミュニティからも この機能を構築してきたので コマンドライン引数の記述が うまくできると分かりました ライブラリにある簡略記法を使って 最小値を求めるなら こんな感じでしょう この文字列は ユーザが何かを渡すものです コマンドラインオプションを とても簡潔に宣言すべきものです ものをくくり出す クリーンな構文のおかげで property wrapperの可能性が広がります 別のセッションでも紹介していますが “View”のデータ依存を表現するため property wrapperを SwiftUIのどこでも使います SwiftUIの中にも property wrapperがあります view local stateを導く“State” 最初のクラスリファレンスのための “Binding”もあります “Environment”も 見たことがあるでしょう こうしたproperty wrapperを Swiftで使う利点は データの場所や宣言の中で どうアクセスされたか ポリシーを述べている点です ビューを構築する時には 気にする必要がありません システムが管理しています Keynoteで 私が触れたスライドを参照できます その数を引き出してください 編集したいなら“$”を使えます
実際のバインディングに 戻ることができます データを扱ったり アップデートの変更を見張ったり データを記憶させる時も ロジックのすべては property wrapperのポリシーの中で 扱われています 実際のデータと 作業の心配だけすればいいのです このスライドで 面白いことがあります $slide.titleがあるのです テキストフィールドで スライドタイトルを編集します
$slideは前述の バックアップの格納型プロパティです そのコンパイラが統合したのは Binding property wrapperを 適用したからです しかし それはバインディングで タイトルがありません タイトルは私が持つ スライドデータのモデルの一部です つまり? そう これはベンが話した property wrapperと Key path member lookupの 結合なのです SwiftUIにあり 最初の クラスリファレンスを提供する バインディングに 焦点を当ててみましょう まずバインディングは property wrapperです つまり標準値があります どんな型も パラメータ表記されています アクセスパターンがどんなものでもです フレームワークが扱うため 分からない部分です しかしバインディングは Generic Subscriptsと共に Key path member lookupも サポートします Generic Subscriptsは 適切な言葉です 実装を知る必要がなく タイプシグネチャを見るべきです 我々が取り入れているのは キーパスです 値型にひも付けられます スライドのように バインディングしていて
エンティティの中で どんなプロパティにもアクセスします 返り値はそこにあるものを 参照するのではなく 新しいバインディングです 外部バインディングの データ依存性を維持する― プロパティに焦点を当てます
見てみましょう 持っていたのはスライドタイプの 1つのバインディングで 最初はこれを そのvalueと考えます スライドのインスタンスを参照できます slide.titleを参照し インスタンスを手に入れます その間 シーンの背後の 修正の跡を追います $slideで そのスライドへの インスタンスを得ます
$slide.titleで そこにはないプロパティを探します コンパイラはこれを dynamicMemberサブスクリプトに 書き換え キーパスをslide.titleに渡します
解決方法は焦点を当てられた バインディングの1つです 外部バインディングのデータ依存性を 持つ文字列プロパティをポイントします 焦点を当ててきた 言語メカニズムから退き ハイレベルのコードが見られるわけです property wrapperのカスタム属性で データ依存性を確立しました データへのアクセスが簡単になり 読んだり修正したりできます バインディングを通して最初の クラスリファレンスに渡すなら $のプレフィックスを その前に置きます 我々は常に他の何かのビューに バインディングを渡します
さて―
ここまで 値方式と 参照方式についてお話しし いつ使うか どう一緒に働かせるかを話しました ジェネリクスと プロトコルの話もしました プロトコルは コードの再利用に使ってください 分類や大きなヒエラルキーは 邪魔になるだけです 必要ありません 最後にproperty wrapperの 言語の特性を使い データへのアクセスを 引き出せるかについて 少し深く見ていきました ありがとうございます 続きはラボで話しましょう (拍手)
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。