lazy var の特徴を知る #cocoa_kansai #cswift

140 views

Published on

きっかけは try! Swift 2016 の HIPSTER SWIFT でした。そこで Hector さんの lazy var について興味深い(当時はどんなに頑張っても意味を汲み取れなかった)話を受け、もう一度 lazy var の特徴を眺めそこから『Hector さんの発していた意味』と『lazy var をどんな風に使ったらいいのかな』みたいなことを考察してみた資料です。

Published in: Engineering
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
140
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

lazy var の特徴を知る #cocoa_kansai #cswift

  1. 1. EZ-NET 熊⾕友宏 http://ez-net.jp/ 2016.03.12 第66回 Cocoa 勉強会勉強会 lazy var の特徴を知る Swift カジュアルプログラミング Swift 2.1.1 2016.04.02 第6回 カジュアル Swift 勉強会
  2. 2. 熊谷友宏 Xcode 5 徹底解説 MOSA Xcode 5 の全機能を
 徹底的に解説した本 OSX/iOS 系の歴史深い
 有料会員制の勉強会 紙版は絶版、電子書籍は販売中 Xcode 7 でも役立つはず 法人会員も多数 @es_kumagai EZ-NET http://ez-net.jp/ 書籍 / 登壇
  3. 3. 熊谷友宏 横浜 iPhone 開発者勉強会 #yidev わいわい・ゆるく、iPhone 開発者の
 みんなで楽しく過ごすのが目的の会 【 横浜・馬車道 】 カジュアル Swift 勉強会 #cswift ゆるくみんなで Swift を語らえる場を
 作りたくて始めた会 【 横浜・青葉台 】 第6回を 2016-04-02 に開催予定 @es_kumagai EZ-NET http://ez-net.jp/ 勉強会
  4. 4. 熊谷友宏 @es_kumagai EZ-NET http://ez-net.jp/ CodePiece iOS, OS X, Apple Watch アプリ ソースコードを Twitter と Gist に同時投稿できる。 いつもの電卓 計算式も見える電卓アプリ。 watchOS 1 対応 音で再配達ゴッド 簡単操作で 再配達の申し込み。 EZ-NET IP Phone iPhone でひかり電話を使う。 自宅 LAN からの利用専用
  5. 5. CodePiece for OS X 勉強会を楽しむアプリ ソースコードを Twitter と Gist に同時投稿できる
 勉強会で知見をみんなと共有したい時とかに便利! できること #cswift
  6. 6. try! Swift 2016.03.02 - 2016.03.04
  7. 7. try! Swift 2016 初日
  8. 8. try! Swift 2016 二日目
  9. 9. try! Swift 2016 最終日
  10. 10. try! Swift The most impressive speaker for me ! 😆 HIPSTER SWIFT ★ @noescape ★ @autoclosure ★ inline lazy vars ★ variadic parameters ★ labeled loops ★ type omitting
  11. 11. HIPSTER SWIFT inline lazy vars
  12. 12. Lazy Variables 初期化を参照時まで遅延できる変数
  13. 13. HIPSTER SWIFT ▶ 値型プロパティを lazy 付きで定義 ▶ 右辺で参照時に設定する値を記載 Lazy Variables class MyClass { lazy var value: Int = { return self.calculate() }() }
  14. 14. HIPSTER SWIFT 評価式の書式 lazy var value: Int = { return self.calculate() }() これが … lazy var value: Int = self.calculate() こう書ける
  15. 15. HOW BEAUTIFUL ... BUT … SAFE ?
  16. 16. class MyClass { lazy var value: Int = self.calculate() } HIPSTER SWIFT 評価式の書式 var obj: MyClass? = MyClass() obj.value obj = nil 循環参照しない 初期化コードで self を使っていても … DEINIT!
  17. 17. AWESOME !! 🎉
  18. 18. class MyClass { lazy var value: Int = self.calculate() } HIPSTER SWIFT 気になったところ Super Lazy Vars オート クロージャー 強く参照 ! 強く参照 ! どういうことだろう… 循環 しない !
  19. 19. Lazy Variables もっと理解して使いたい
  20. 20. Lazy Variables ▶ 初期値の設定を参照直前まで延期できる ▶ 主に保存型プロパティで使用 概要 // 定義の仕方 class File { lazy var content: Content = Content(path: somePath) }
  21. 21. File Lazy Variables 初期化タイミング = Content(path: somePath) Contents lazy var 参照 値 instance Contents
  22. 22. File Lazy Variables 自身の機能で初期化できる = Content(path: self.path) Contents lazy var 参照 instance Contents 参照のときは インスタンス生成済みなので 初期化処理で self も使える
  23. 23. プロパティを自分自身の機能で 初期化したいときに便利 … なもの? そう単純なものでもなさそう Lazy Variables 使いどころ?
  24. 24. 挙動
  25. 25. Lazy Variables ▶ 参照のときに初期化が行われる ▶ 初期化して保存してすぐ参照する 挙動 1/7 … 初期化のタイミング class File { lazy var content: Content = Content(path: somePath) } // ここでは content は初期化されない let file = File() // ここで初めて content が初期化され、値がプロパティに保持される let content = file.content
  26. 26. Lazy Variables ▶ 初回参照時に値が設定される ▶ 以降参照時は初回初期化時の値を取得 挙動 2/7 … 初期化後は既存の値を取得 class File { lazy var content: Content = Content(path: self.path) } let file = File() let content1 = file.content // いったん初期化が行われると file.path = newPath // 関係するものを更新しても let content2 = file.content // 得られる値は連動しない
  27. 27. Lazy Variables ▶ 値は自由に代入できる ▶ 初期化コードとは無関係に設定可能 挙動 3/7 … 自由に代入可能 class File { lazy var content: Content = Content(path: somePath) } // ここでは content は初期化されない let file = File() // 自分で値を自由に設定できる file.content = Content(path: anotherPath)
  28. 28. Lazy Variables ▶ 参照しなければ初期化されない ▶ 使わなければ初期化処理を省略できる 挙動 4/7 … 初期化の省略 class File { lazy var content: Content = Content(path: somePath) } // ここでは content は初期化されない let file = File() // その後 content を参照しなければ、初期化は行われない
  29. 29. Lazy Variables ▶ 未初期化のときにだけ初期化される ▶ 参照前の代入も初期化とみなす 挙動 5/7 … 初期化コードを使わない初期化 class File { lazy var content: Content = Content(path: somePath) } let file = File() // 参照前に content に値を代入すると … file.content = Content(path: anotherPath) // 初めての参照時でも初期化コードは実行されない let content = file.content
  30. 30. Lazy Variables ▶ 初期化コードは参照直前に実行 ▶ 意識的に初期化完了を待つ必要なし 挙動 6/7 … 初期化は同期的に実施 class File { lazy var content: Content = Content(path: somePath) } let file = File() // 初めての content 参照 let content = file.content // 初期化して self.content = Content(path: somePath) // 取得する return self.content
  31. 31. Lazy Variables ▶ プロトコルが求めるプロパティに対して使える ▶ インスタンス生成後まで初期化を延期できる 挙動 7/7 … プロトコルに対する実装で使用可能 protocol MyProtocol { // 非オプショナルな型を求められても var property: Int { get } } class MyObject : MyProtocol { // 初期化をインスタンス生成後まで遅延可能 lazy var property: Int = self.updateProperty() }
  32. 32. 特徴と利用場面
  33. 33. 参照直前まで 既定値の代入を遅延できるプロパティ Lazy Variables 特徴
  34. 34. Lazy Variables 利用場面 ▶ 計算に時間のかかるプロパティで ▶ 必ず使うとは限らないが ▶ 使うなら各所で何度も参照する場面 ▶ 自身の値で初期化したいプロパティで、 ▶ 明確な初期化タイミングを定められず、 ▶ 自身の値が変化しても影響を受けず、 ▶ 別の値に書き換える道も提供したい場面
  35. 35. 注意
  36. 36. Lazy Variables ▶ ゲッターも mutating 扱い ▶ 構造体だと var に格納しないと参照できない 注意 1/4 struct File { lazy var content: Content = Content(path: somePath) } let file = File() let content = file.content cannot use mutating getter on immutable value: 'file' is a 'let' constant
  37. 37. Lazy Variables ▶ 初期化以降の状態変化は連動しない ▶ 初期化後の状態と矛盾する可能性 注意 2/4 class File { lazy var content: Content = Content(path: self.path) } let file = File() let content1 = file.content // 現在のパスから初期化されたら file.path = newPath // その後にパスが変更されても let content2 = file.content // 古いパスのコンテンツが得られる
  38. 38. Lazy Variables ▶ 遅延初期化を除き、普通のプロパティと同等 ▶ 初期化式と無関係な独自の値を自由に設定可能 注意 3/4 class File { lazy var content: Content = Content(path: somePath) } let file = File() // 初期化式とは無関係な値をいつでも設定できる file.content = Content(text: "TEXT 1") // 何度でも設定できる file.content = Content(text: "TEXT 2")
  39. 39. Lazy Variables ▶ 予測困難な初期化タイミング ▶ マルチスレッドで扱うときに悲劇を生むかも 注意 4/4 class File { lazy var content: Content = Content(path: somePath) } // どちらで content が初期化される? func pushButton(sender: AnyObject) { contentView.content = file.content } func updateNotification(notification: NSNotification) { contentView.content = file.content }
  40. 40. HIPSTER SWIFT オート クロージャー 強く参照 ! 強く参照 ! 循環 しない ! strong reference
  41. 41. class MyClass { lazy var calculate1: () -> Int = self.calculate3 lazy var calculate2: () -> Int = { () -> Int in return self.calculate3() } func calculate3() -> Int { return 10 } } let obj = MyClass() obj.calculate1() // 循環参照する obj.calculate2() // 循環参照する obj.calculate3() // 循環参照しない HIPSTER SWIFT 循環参照を起こす例
  42. 42. 関連する話題 lazy var から広がる話
  43. 43. 大域変数
  44. 44. 大域変数も lazy
  45. 45. 大域変数 ▶ 初めて使うタイミングで初期化される ▶ 内部的に lazy var として扱われる 遅延初期化 var bundle = NSBundle.mainBundle() class File { init() { self.content = Content(path: bundle.bundlePath) } } ここで初めて 初期化される
  46. 46. 大域変数 ▶ 大域変数は let で宣言できる ▶ 既定値にしかならないことを保証可能 既定値を保証 let bundle = NSBundle.mainBundle() class File { init() { bundle = NSBundle(forClass: Object.self) } } Cannot assign to value: 'bundle' is a 'let' constant
  47. 47. 大域変数 ▶ 大域変数はモジュールの名前空間に所属 ▶ 他のモジュールと衝突しない 名前空間の活用 public let defaultManager = ... func method() { let manager = MyModule.defaultManager } MyModule で定義 アプリで使用
  48. 48. lazy var と似ている機能
  49. 49. ImplicitlyUnwrappedOptional ( Type! )
  50. 50. ImplicitlyUnwrappedOptional ▶ 値が無いことを表現できる型 ▶ 値がある前提で扱える 概要 // 値が無いまま存在させて、 var content: Content! = nil // 値を後から設定できる content = Content(path: path) // 普通の変数のように操作する doSomethingWithData(content.data) 値がなければ 強制終了
  51. 51. 使う時には 値が入っていることを約束する プログラマーが! ImplicitlyUnwrappedOptional 意図
  52. 52. ImplicitlyUnwrappedOptional ▶ 初期化を遅らせることができる ▶ 初期化されているものとして使用できる lazy var と似ているところ ▶ 適切なタイミングで初期化が必要 lazy var と決定的に異なるところ 初期化のタイミングを 想定できる! 特徴
  53. 53. 初期化タイミングを制御できるなら ImplicitlyUnwrappedOptional
  54. 54. lazy var × ImplicitlyUnwrappedOptional
  55. 55. lazy var × ImplicitlyUnwrappedOptional ▶ 初期化を参照時まで遅らせる ▶ 初期値を決めておける lazy var ▶ 使うときに値が入っていることを約束する ▶ 値がないことを表現できる ImplicitlyUnwrappedOptional 性質
  56. 56. ゾンビっぽい変数が生まれる 😱 ※ ただしプロパティでの使用に限る
  57. 57. class File { lazy var content: Content! = Content(path: self.path) } let file = File() // content を参照すると初期化される let content = file.content // ImplicitlyUnwrappedOptional なので nil を入れられる file.content = nil // nil のときに再び content を参照すると再初期化される let content = file.content lazy var × ImplicitlyUnwrappedOptional 値を消しても復活する変数
  58. 58. 画像キャッシュの機能などに 応用できるかもしれない
  59. 59. lazy 超かっこいい … 😆
  60. 60. lazy var ▶ 初期化されるタイミングが読めない ▶ 読み書きともに mutating 扱い ▶ 複数スレッドで衝突する可能性 ▶ 初期化式とは無関係に値を入れられる 注意したいところ ▶ 単に “便利だから” という理由で使わない ▶ 初期化を遅らせたいだけなら
 ImplicitlyUnwrappedOptional も検討したい
  61. 61. 魔力にご用心 lazy の
  62. 62. まとめ lazy var の特徴を知る 1. HIPSTER SWIFT 2. Lazy Variables の動き 3. 大域変数も lazy 扱い 4. ImplicitlyUnwrappedOptional は
 動きが lazy var と似ている 5. lazy var × ImplicitlyUnwrappedOptional で
 何度だって蘇る変数が生まれる 6. lazy の魔力にご用心

×