ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
SwiftにおけるARC: 基礎とその先
SwiftにおけるオブジェクトのライフタイムとARCの基本について確認しましょう。どのような言語機能がオブジェクトのライフタイムを観測可能にするのか、観測されたオブジェクトのライフタイムに依存した結果と、それを修正する安全なテクニックについて深く掘り下げます。
リソース
-
ダウンロード
♪ (SwiftにおけるARC: 基礎とその先) こんにちは私の名前はMeghanaです 今日はSwiftのARCについてお話します Swiftは構造体や列挙型などの 強力な値型を提供します 参照型が付属していて意図しない 共有の危険を回避するため 可能な場合は値型を 使用することをお勧めします クラスはSwiftの参照型であり あなたがそれらを使用することにした場合 Swiftは自動参照カウントかARCを介して 管理します 効果的なSwiftを書くために ARCがどのように機能するかを 理解することが重要です このセッションではまさにそれを行います オブジェクトの存続期間とSwiftのARCの レビューから始めます 次に観測可能なオブジェクトの 存続期間について説明します 以下について詳しく説明していきます オブジェクトの存続期間を 観察可能にする言語機能は何か 観察されたオブジェクトの 存続期間に依存することの結果 そしてそれらを修正するための いくつかの安全なテクニックです さぁ始めましょう Swiftでのオブジェクトの 存続期間は初期化から始まります そして最後の使用で終了します 存続期間が終了した後にオブジェクトの割り当てを 解除することでARCは自動的に メモリを管理します その参照カウントを追跡することによって オブジェクトの存続期間を決定します ARCは主に保持および解放操作を挿入する Swiftコンパイラによって駆動されます 実行時に 保持は参照カウントを1つ加算させ そしてリリースはそれを1つ減算させます 参照カウントがゼロに下がると オブジェクトの割り当てが解除されます 例を使ってどのように機能するかを見てみましょう 旅行Appを作成したいとします 旅行者を表すために名前を付けやオプションの 目的地プロパティでクラスを作成しましょう test()関数では まずTravelerオブジェクトが作成されます 次にの参照がコピーされます そして最後にその目的地が更新されます 自動的に管理するために Travelerオブジェクトのメモリ Swiftコンパイラは保持操作を挿入します 参照が開始されリリース操作が行われたとき 参照を最後に使用した後です traveller1はTravelerオブジェクトへの 最初の参照です そしてその最後の用途はコピーです traveller1参照を最後に 使用した直後ここで swiftコンパイラはリリース操作を挿入します 参照が始まると保持操作は 挿入されません 初期化によって参照カウントが 1に設定されるためです traveller2はTraveler オブジェクトへの別の参照で 最後に使用するのは目的地の更新です 参照が開始されリリース操作が行われたとき 参照を最後に使用した直後 ここでSwiftコンパイラは 保持操作を挿入します コードをステップスルーし実行時に 何が起こるかを見てみましょう まずTravelerオブジェクトが ヒープ上に作成され 参照カウント1で初期化されます 次に新しいリファレンスの準備として 保持操作が実行され 参照カウントを2に1つ加算されます 今や traveler2もTraveler オブジェクトの 参照になります traveller1リファレンスを 最後に使用した後 リリース操作が実行され 参照カウントを1に1つ減算されます 次にTravelerオブジェクトの目的地 BigSurに更新されます それがtraveler2リファレンスの 最後の使用だったので リリース操作が実行され 参照カウントをゼロに1つ減算します 参照カウントがゼロに下がると オブジェクトの割り当てを解除できます Swiftのオブジェクトの 有効期間は使用ベースです オブジェクトの保証された最小の存続期間は 初期化で始まり最後の使用で終わります これはC ++などの言語とは異なり closing braceでオブジェクトの 存続期間が終了することが 保証されています この例では最後に使用した 直後オブジェクトの割り当てが 解除されていることがわかりました ただ実際にはオブジェクトの存続期間は 保持および解放操作によって決定され Swiftコンパイラによって挿入されます そして開始するARC最適化に応じて 観測されたオブジェクトの存続期間は 保証された最小値で異なる場合があります オブジェクトの最後の使用を超えて終了します このような場合オブジェクトは プログラムポイントで その最後の使用を超えて割り当て解除されます ほとんどの場合オブジェクトの 正確な存続期間は何か それは本当の問題ではありません ただし弱い参照や所有されていない参照や 初期化解除の予想外の結果などの 言語機能を使用する場合 オブジェクトの存続期間を観察することが可能です そしてあなたがプログラムを持っているなら 保証されたオブジェクトの存続期間の代わりに 観測されたオブジェクトの存続期間に依存し 将来的に問題が発生する可能性があります 観測されたオブジェクトの 存続期間に依存しているため 今日はうまくいくかもしれませんがそれは偶然です 観測されたオブジェクトの存続期間 はSwiftコンパイラの 緊急のプロパティです実装の詳細が変更されたとき 変更することができます このようなバグは開発中に発見されない場合があり 長い間隠されたままになる可能性がありますが ARCの最適化が改善された コンパイラのアップデートや 関連性のないソースの変更によって 以前は制限されていた ARCの最適化が可能になることが 明らかになります オブジェクトの存続期間を観測可能にする 言語機能を説明し観測された オブジェクトの寿命だけに頼ると どのようなことが起こるかを説明し それを解決するための安全な テクニックを紹介します 強い参照であるデフォルトの参照とは異なり 弱い参照や所有していない参照は 参照カウントに参加しません そのため一般的には参照サイクルを 壊すために使用されます 詳細に入る前に 参照サイクルとは何かを見てみましょう これは私たちの旅行Appの拡張です オプションのポイント制を導入したいと思います 旅行者はアカウントを持って ポイントを貯めることができます これを表すためにポイントプロパティ付きの 新しいアカウントクラスがあります AccountクラスはTraveler クラスを指します またTravelerクラスは Accountクラスを参照します テスト関数はTravelerオブジェクトと Accountオブジェクトを作成し 次に旅行者の参照を介して printSummary() 関数を呼び出します コードをステップスルーして ARCで何が起こるかを見ましょう まずTravelerオブジェクトが ヒープ上に作成されます 参照カウントは1です 次に アカウントオブジェクトが 参照カウントは1で ヒープ上に作成されます AccountオブジェクトはTraveler オブジェクトを参照しているため Travelerオブジェクト参照 カウントが2に1つ加算されます これでTravelerオブジェクトは Accountオブジェクトの参照を開始し Accountオブジェクトの参照カウントは また2に1つ加算されます これはアカウント参照の最後の使用です この後アカウント参照はなくなります アカウントオブジェクトの参照カウントは 1に1つ減算されます 次に名前とポイントを 印刷するrintSummary() 関数が呼び出されます これはTravelerリファレンスの 最後の使用法です この後Travelerリファレンスは消え およびTravelerオブジェクトの 参照カウントは 1に1つ減算されます オブジェクトを到達可能にする すべての参照の後で消えても オブジェクトの参照カウントは1のままで これは基準サイクルによるものです その結果オブジェクトの 割り当てが解除されることはなく メモリリークを引き起こします 弱い参照または所有されていない参照で 基準サイクルを壊す可能性があります それらは参照カウントに参加していないので 弱い参照または所有されていない 参照が使用されている間 参照されたオブジェクトの 割り当ての解除の場合があります これが起こるときSwiftランタイムは 弱参照へのアクセスをnilとして およびトラップとして 所有されていない参照へ アクセスを安全に切り替えます 参照サイクルに参加している参照は 参照サイクルを中断するため 弱いまたは所有されていないもの としてマークすることができます Appによって異なります この例では旅行者の参照に Accountクラスでは弱いものとして マークを付けましょう 弱参照が参加しないため 参照カウントでは Travelerオブジェクトを最後に使用した後 その参照カウントはゼロになります Travelerオブジェクトの 参照カウントがゼロになると 割り当てを解除できます Travelerオブジェクトがなくなると Accountオブジェクトへの参照がなくなり 参照カウントをゼロにします これでAccountオブジェクトの 割り当てを解除できます この例では弱参照を使用しました 基準サイクルを壊すだけです 保証されているオブジェクトの 存続期間が終了しているときに 弱い参照を使ってオブジェクトにアクセスした場合 そのオブジェクトが利用できるか どうかを観測されたオブジェクトの 存続期間に依存していると 将来観測されたオブジェクトの存続期間が 無関係な理由で変更されたときに バグが発生する可能性があります 例を見てみましょう ここでprintSummary()関数は TravelerクラスからAccountクラス に移動されます そしてtest()関数は printSummary()関数を アカウント参照を介して 呼び出すようになりました printSummary()関数が 呼び出されたとき 正確に何が起こるでしょう? 今日は旅行者の名前とポイントが 印刷される場合がありますが しかし これは単なる偶然です これはTravelerオブジェクトが 最後に使用されたためで printSummary()関数を 呼び出す前です この後Travelerオブジェクトの 参照カウントが コンパイラがリリースを 挿入した場合最後に使用した直後 ゼロに低下する可能性があります 参照カウントがゼロに低下した場合 弱参照を介したTraveler オブジェクトへのアクセス がゼロになりまた Travelerオブジェクトの割り当てが 解除される場合があります したがってprintSummary() 関数が呼び出されると 弱いTraveler参照の 強制アンラップはトラップし クラッシュを引き起こします あなたは疑問に思うかもしれません フォースアンラップがここでの クラッシュの理由である場合 オプションのバインディングが それを妨げている可能性があります オプションのバインディングは 実際に問題を悪化させます 明らかなクラッシュがなければ サイレントバグが発生します 無関係な理由で観測された オブジェクトの存続期間が変わると 気付かない可能性があります 弱くて所有権のない参照を 安全に処理するためにはさまざまな技術があります それぞれの技術には 導入時のコストと 継続的なメンテナンスのコストがあります 私たちの例でそれらを一つずつ調べてみましょう SwiftはwithExtendedLifetime ()ユーティリティの提供で オブジェクトの寿命を明示的に延長できます withExtendedLifetime ()を使用すると Travelerオブジェクトの存続期間を 安全に延長するには printSummary()関数が 呼び出されている間 潜在的なバグを防ぎます 空の呼び出しをかけることでも 同じ効果が得られます 既存のスコープの最後に withExtendedLifetime()を追加します より複雑なケースについては オブジェクトの存続期間を延ばし deferを使用して現在のスコープの 最後までコンパイラに問い合わせることができます withExtendedLifetime()は オブジェクトの存続期間の バグを回避する簡単な方法のように見えます ただし この手法は壊れやすく 正しさの責任があなたに移ります このアプローチでは 弱い参照がバグを引き起こす可能性があるたびに withExtendedLifetime()の 使用を確認する必要があります 制御されていない場合 withExtendedLifetime()は コードベース至る所に忍び寄り メンテナンスコストの増加させます より優れたAPIを使用したクラスの再設計 はるかに理にかなったアプローチです 強い参照のみにオブジェクトへの アクセスを制限できる場合 オブジェクトの存続期間の 不意打ちを防ぐことができます ここではトラベラークラスへ printSummary()関数が元に戻され Accountクラスの弱参照は 非表示になっています テストは強力な参照を介して printSummary()関数を 呼び出すように強制されるようになり 潜在的なバグを排除します パフォーマンスコストを負担することに加えて クラスの設計に注意しない場合 弱くて所有されていない参照は バグを公開する可能性があります 一時停止して考えることが重要です なぜ弱くて所有されていない参照が必要なのか? それらは参照サイクルを中断する ためにのみ使用されますか? そもそも参照サイクルの作成を回避した場合は どうなりますか? 参照サイクルはアルゴリズムを再考や ツリーの構造への循環クラス関係の変換で 回避できることがよくあります この例では トラベラークラスはアカウント クラスを参照する必要があります Accountクラスには実際には必要ありません Travelerクラスを参照します アカウントクラスは旅行者の個人情報に アクセスのみが必要です PersonalInfoと 呼ばれる新しいクラスに 旅行者の個人情報を移動できます トラベラークラスとアカウント クラスの両方が参照できます サイクルを回避して PersonalInfoクラスに追加します 弱い参照や所有されていない 参照の必要性を回避することは 追加の実装コストがかかる場合がありますが しかしこれは明確な方法です すべての潜在的なオブジェクトの 存続期間のバグを排除します オブジェクトの存続期間が 観察可能になる別の言語機能 deinitializerの副作用です deinitializerは割り当て 解除の前に実行され 外部プログラムの効果により その副作用を観察することができます deinitializerの副作用と 外部プログラムの効果を 連続させるようにコードを書くと 隠れたバグが発生する可能性があり 観測されたオブジェクトの存続期間 が無関係な理由で変化したときに 初めて発見されることになります このようなバグがどのように 発生するかを説明する前に deinitializer とは何かを 見てみましょう これは deinitializerを使用した 最初の例の繰り返しです deinitializerにはグローバルな 副作用があり コンソールにメッセージを印字します 今日「Donetraveling」が 印字された後に deinitializerが実行される 可能性があります Travelerオブジェクトを 最後に使用してからとは 目的地の更新であり 開始する ARC最適化によって異なりますが 「Donetraveling」が印字される前に deinitializer を実行できます この例では deinitializer の副作用は観察可能 でしたが信頼されていませんでした より複雑な例を見てみましょう deinitializer の副作用が 外部プログラムの効果により 依存している場合です ここでTravelerクラスに旅行 メトリックを導入します 行き先が更新されるたびに TravelMetricsクラスに記録されます 最終的にTravelerオブジェクトを 非初期化すると メトリックはグローバルレコードに公開されます 公開されているメトリックは旅行者の匿名IDと 調べた行き先の数 そして計算された旅行の関心カテゴリです test()関数では まずTravelerオブジェクトが作成されます 次にtravelMetricsを参照し Travelerオブジェクトからコピーされます 旅行者の目的地がBig Surに更新され これはTravelMetricsに Big Surを記録します 旅行者の目的地がカタリナに更新され カタリナをTravelMetricsに 記録します 次に記録された目的地を見ることで 旅行の関心カテゴリが計算されます 今日旅行の関心を計算した後 deinitializerが実行される 可能性があります 関心のあるカテゴリを Natureとして公開します しかしTravelerオブジェクトの 最後の使用は カタリナへの宛先アップデートで 直後にデイニシャライザーを実行できます deinitializer が実行されるため 旅行の関心を計算する前にnilが公開されます バグを引き起こします 弱くて所有されていない参照のように deinitializer の副作用を 安全に処理する さまざまなテクニックがあります それぞれの先行実装コストの程度 対継続的なメンテナンスコストの程度は さまざまです それらを一つずつ見ていきましょう Travelerオブジェクトの有効期間を 旅行の関心カテゴリが計算 されるまで明示的に延長するため withExtendedLifetime() を使用でき 潜在的なバグを防ぎます 前に説明したように このアプローチではあなたに正しさの 責任を移し deinitializerの副作用とおよび 外部プログラムの影響間の誤った 相互作用でメンテナンスコストが増加する 可能性があるたびに使用される withExtendedLifetimeを 確認する必要があります 効果がすべて局所的である場合 deinitializerの副作用は 観察できません 内部クラスの詳細把握で可視性を 制限するクラスAPIの再設計は オブジェクトの存続期間の バグを防ぐことができます ここでTravelMetricsは 外部アクセスから隠された プライベートとしてマークされています deinitializerが最も関心のある 旅行カテゴリを計算するようになり メトリックを公開します これは機能しますが より理にかなったアプローチは deinitializerの副作用の すべてを取り除くことです ここではdeinitializerの代わりに メトリックを公開するには deferが使用されて deinitializerは 検証のみを実行します デイニシャライザーの副作用を取り除くことにより オブジェクトの存続期間に関する 潜在的なバグをすべて排除できます 教育的な旅行Appの例で ARCと弱い参照と所有されていない参照 およびdeinitializerの 副作用について学びました オブジェクトの存続期間を観察可能にする 言語機能を完全に理解することが重要です 潜在的に誤った観測されたオブジェクトの 存続期間についての依存を排除し 意外な時にバグを発見しないようにします xcode 13では 「オブジェクトの寿命の最適化」 と呼ばれる新しい実験的なビルド設定が Swiftコンパイラで利用できます これにより強力な存続期間 短縮ARC最適化が可能になります このビルド設定をオンにすると オブジェクトの割り当ての 解除が見える場合があります 最後の使用直後ははるかに一貫して 観測されたオブジェクトの保証された最小値に近い 存続期間をもたらします これで隠しオブジェクト存続期間の バグが明らかになる可能性があり 説明した例と同様です あなたは安全なテクニックに従うことができます このセッションではこれらのバグを すべて排除するために説明しました このセッションを楽しんでいただけたでしょうか 見てくれてありがとう ♪
-
-
1:49 - ARC Example
class Traveler { var name: String var destination: String? } func test() { let traveler1 = Traveler(name: "Lily") let traveler2 = traveler1 traveler2.destination = "Big Sur" print("Done traveling") }
-
6:37 - Reference Cycle Example
class Traveler { var name: String var account: Account? func printSummary() { if let account = account { print("\(name) has \(account.points) points") } } } class Account { var traveler: Traveler var points: Int } func test() { let traveler = Traveler(name: "Lily") let account = Account(traveler: traveler, points: 1000) traveler.account = account traveler.printSummary() }
-
9:05 - Weak Reference Example
class Traveler { var name: String var account: Account? func printSummary() { if let account = account { print("\(name) has \(account.points) points") } } } class Account { weak var traveler: Traveler? var points: Int } func test() { let traveler = Traveler(name: "Lily") let account = Account(traveler: traveler, points: 1000) traveler.account = account traveler.printSummary() }
-
10:05 - Accessing an object via weak reference
class Traveler { var name: String var account: Account? } class Account { weak var traveler: Traveler? var points: Int func printSummary() { print("\(traveler!.name) has \(points) points") } } func test() { let traveler = Traveler(name: "Lily") let account = Account(traveler: traveler, points: 1000) traveler.account = account account.printSummary() }
-
11:14 - Accessing an object via optional binding of weak reference
class Traveler { var name: String var account: Account? } class Account { weak var traveler: Traveler? var points: Int func printSummary() { if let traveler = traveler { print("\(traveler.name) has \(points) points") } } } func test() { let traveler = Traveler(name: "Lily") let account = Account(traveler: traveler, points: 1000) traveler.account = account account.printSummary() }
-
11:45 - Safe techniques for handling weak references - withExtendedLifetime()
func test() { let traveler = Traveler(name: "Lily") let account = Account(traveler: traveler, points: 1000) traveler.account = account withExtendedLifetime(traveler) { account.printSummary() } } func test() { let traveler = Traveler(name: "Lily") let account = Account(traveler: traveler, points: 1000) traveler.account = account account.printSummary() withExtendedLifetime(traveler) {} } func test() { let traveler = Traveler(name: "Lily") let account = Account(traveler: traveler, points: 1000) defer {withExtendedLifetime(traveler) {}} traveler.account = account account.printSummary() }
-
12:55 - Safe techniques for handling weak references - Redesign to access via strong reference
class Traveler { var name: String var account: Account? func printSummary() { if let account = account { print("\(name) has \(account.points) points") } } } class Account { private weak var traveler: Traveler? var points: Int } func test() { let traveler = Traveler(name: "Lily") let account = Account(traveler: traveler, points: 1000) traveler.account = account traveler.printSummary() }
-
14:20 - Safe techniques for handling weak references - Redesign to avoid weak/unowned reference
class PersonalInfo { var name: String } class Traveler { var info: PersonalInfo var account: Account? } class Account { var info: PersonalInfo var points: Int }
-
15:23 - Deinitializer Example
class Traveler { var name: String var destination: String? deinit { print("\(name) is deinitializing") } } func test() { let traveler1 = Traveler(name: "Lily") let traveler2 = traveler1 traveler2.destination = "Big Sur" print("Done traveling") }
-
16:10 - Sequencing deinitializer side-effects with external program effects
class Traveler { var name: String var id: UInt var destination: String? var travelMetrics: TravelMetrics // Update destination and record travelMetrics func updateDestination(_ destination: String) { self.destination = destination travelMetrics.destinations.append(self.destination) } // Publish computed metrics deinit { travelMetrics.publish() } } class TravelMetrics { let id: UInt var destinations = [String]() var category: String? // Finds the most interested travel category based on recorded destinations func computeTravelInterest() // Publishes id, destinations.count and travel interest category func publish() } func test() { let traveler = Traveler(name: "Lily", id: 1) let metrics = traveler.travelMetrics ... traveler.updateDestination("Big Sur") ... traveler.updateDestination("Catalina") metrics.computeTravelInterest() } verifyGlobalTravelMetrics()
-
17:56 - Safe techniques for handing deinitalizer side effects - withExtendedLifetime()
func test() { let traveler = Traveler(name: "Lily", id: 1) let metrics = traveler.travelMetrics ... traveler.updateDestination("Big Sur") ... traveler.updateDestination("Catalina") withExtendedLifetime(traveler) { metrics.computeTravelInterest() } }
-
class Traveler { ... private var travelMetrics: TravelMetrics deinit { travelMetrics.computeTravelInterest() travelMetrics.publish() } } func test() { let traveler = Traveler(name: "Lily", id: 1) ... traveler.updateDestination("Big Sur") ... traveler.updateDestination("Catalina") }
-
class Traveler { ... private var travelMetrics: TravelMetrics func publishAllMetrics() { travelMetrics.computeTravelInterest() travelMetrics.publish() } deinit { assert(travelMetrics.published) } } class TravelMetrics { ... var published: Bool ... } func test() { let traveler = Traveler(name: "Lily", id: 1) defer { traveler.publishAllMetrics() } ... traveler.updateDestination("Big Sur") ... traveler.updateDestination("Catalina") }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。