lazy var の特徴を知る #cocoa_kansai #cswift
TRANSCRIPT
EZ-NET 熊⾕友宏 http://ez-net.jp/
2016.03.12 第66回 Cocoa 勉強会勉強会
lazy var の特徴を知るSwift カジュアルプログラミング
Swift 2.1.1
2016.04.02 第6回 カジュアル Swift 勉強会
熊谷友宏
Xcode 5 徹底解説 MOSA
Xcode 5 の全機能を徹底的に解説した本
OSX/iOS 系の歴史深い有料会員制の勉強会
紙版は絶版、電子書籍は販売中
Xcode 7 でも役立つはず 法人会員も多数
@es_kumagai EZ-NET http://ez-net.jp/
書籍 / 登壇
熊谷友宏
横浜 iPhone 開発者勉強会#yidev
わいわい・ゆるく、iPhone 開発者のみんなで楽しく過ごすのが目的の会
【 横浜・馬車道 】カジュアル Swift 勉強会
#cswift
ゆるくみんなで Swift を語らえる場を作りたくて始めた会
【 横浜・青葉台 】
第6回を 2016-04-02 に開催予定
@es_kumagai EZ-NET http://ez-net.jp/
勉強会
熊谷友宏@es_kumagai EZ-NET http://ez-net.jp/
CodePiece
iOS, OS X, Apple Watch アプリ
ソースコードを Twitter と Gist に同時投稿できる。
いつもの電卓
計算式も見える電卓アプリ。 watchOS 1 対応
音で再配達ゴッド
簡単操作で 再配達の申し込み。
EZ-NET IP Phone
iPhone でひかり電話を使う。 自宅 LAN からの利用専用
CodePiece for OS X勉強会を楽しむアプリ
ソースコードを Twitter と Gist に同時投稿できる 勉強会で知見をみんなと共有したい時とかに便利!
できること
#cswift
try! Swift2016.03.02 - 2016.03.04
try! Swift2016 初日
try! Swift2016 二日目
try! Swift2016 最終日
try! SwiftThe most impressive speaker for me ! 😆
HIPSTER SWIFT
★ @noescape ★ @autoclosure ★ inline lazy vars ★ variadic parameters ★ labeled loops ★ type omitting
HIPSTER SWIFTinline lazy vars
Lazy Variables初期化を参照時まで遅延できる変数
HIPSTER SWIFT
▶ 値型プロパティを lazy 付きで定義 ▶ 右辺で参照時に設定する値を記載
Lazy Variables
class MyClass {
lazy var value: Int = { return self.calculate() }()
}
HIPSTER SWIFT評価式の書式
lazy var value: Int = { return self.calculate() }()
これが …
lazy var value: Int = self.calculate()
こう書ける
HOW BEAUTIFUL ...
BUT … SAFE ?
class MyClass {
lazy var value: Int = self.calculate()
}
HIPSTER SWIFT評価式の書式
var obj: MyClass? = MyClass()
obj.value
obj = nil
循環参照しない
初期化コードで self を使っていても …
DEINIT!
AWESOME !! 🎉
class MyClass {
lazy var value: Int = self.calculate()
}
HIPSTER SWIFT気になったところ
Super Lazy Vars
オート クロージャー
強く参照 !
強く参照 !
どういうことだろう…
循環 しない !
Lazy Variablesもっと理解して使いたい
Lazy Variables
▶ 初期値の設定を参照直前まで延期できる ▶ 主に保存型プロパティで使用
概要
// 定義の仕方
class File { lazy var content: Content = Content(path: somePath) }
File
Lazy Variables初期化タイミング
= Content(path: somePath)
Contents
lazy var
参照
値
instance
Contents
File
Lazy Variables自身の機能で初期化できる
= Content(path: self.path)
Contents
lazy var
参照
instance
Contents
参照のときは インスタンス生成済みなので 初期化処理で self も使える
プロパティを自分自身の機能で初期化したいときに便利 … なもの?
そう単純なものでもなさそう
Lazy Variables使いどころ?
挙動
Lazy Variables
▶ 参照のときに初期化が行われる ▶ 初期化して保存してすぐ参照する
挙動 1/7 … 初期化のタイミング
class File { lazy var content: Content = Content(path: somePath) }
// ここでは content は初期化されない let file = File()
// ここで初めて content が初期化され、値がプロパティに保持される let content = file.content
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 // 得られる値は連動しない
Lazy Variables
▶ 値は自由に代入できる ▶ 初期化コードとは無関係に設定可能
挙動 3/7 … 自由に代入可能
class File { lazy var content: Content = Content(path: somePath) }
// ここでは content は初期化されない let file = File()
// 自分で値を自由に設定できる file.content = Content(path: anotherPath)
Lazy Variables
▶ 参照しなければ初期化されない ▶ 使わなければ初期化処理を省略できる
挙動 4/7 … 初期化の省略
class File { lazy var content: Content = Content(path: somePath) }
// ここでは content は初期化されない let file = File()
// その後 content を参照しなければ、初期化は行われない
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
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
Lazy Variables
▶ プロトコルが求めるプロパティに対して使える ▶ インスタンス生成後まで初期化を延期できる
挙動 7/7 … プロトコルに対する実装で使用可能
protocol MyProtocol { // 非オプショナルな型を求められても var property: Int { get } }
class MyObject : MyProtocol { // 初期化をインスタンス生成後まで遅延可能 lazy var property: Int = self.updateProperty()
}
特徴と利用場面
参照直前まで既定値の代入を遅延できるプロパティ
Lazy Variables特徴
Lazy Variables利用場面
▶ 計算に時間のかかるプロパティで ▶ 必ず使うとは限らないが ▶ 使うなら各所で何度も参照する場面
▶ 自身の値で初期化したいプロパティで、 ▶ 明確な初期化タイミングを定められず、 ▶ 自身の値が変化しても影響を受けず、 ▶ 別の値に書き換える道も提供したい場面
注意
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
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 // 古いパスのコンテンツが得られる
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")
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 }
HIPSTER SWIFT
オート クロージャー
強く参照 !
強く参照 !
循環 しない !
strong reference
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循環参照を起こす例
関連する話題lazy var から広がる話
大域変数
大域変数も lazy
大域変数
▶ 初めて使うタイミングで初期化される ▶ 内部的に lazy var として扱われる
遅延初期化
var bundle = NSBundle.mainBundle()
class File {
init() { self.content = Content(path: bundle.bundlePath) } }
ここで初めて 初期化される
大域変数
▶ 大域変数は let で宣言できる ▶ 既定値にしかならないことを保証可能
既定値を保証
let bundle = NSBundle.mainBundle()
class File {
init() { bundle = NSBundle(forClass: Object.self) } }
Cannot assign to value: 'bundle' is a 'let' constant
大域変数
▶ 大域変数はモジュールの名前空間に所属 ▶ 他のモジュールと衝突しない
名前空間の活用
public let defaultManager = ...
func method() {
let manager = MyModule.defaultManager }
MyModule で定義
アプリで使用
lazy var と似ている機能
ImplicitlyUnwrappedOptional( Type! )
ImplicitlyUnwrappedOptional
▶ 値が無いことを表現できる型 ▶ 値がある前提で扱える
概要
// 値が無いまま存在させて、 var content: Content! = nil
// 値を後から設定できる content = Content(path: path)
// 普通の変数のように操作する doSomethingWithData(content.data)
値がなければ 強制終了
使う時には
値が入っていることを約束する
プログラマーが!
ImplicitlyUnwrappedOptional意図
ImplicitlyUnwrappedOptional
▶ 初期化を遅らせることができる ▶ 初期化されているものとして使用できる
lazy var と似ているところ
▶ 適切なタイミングで初期化が必要
lazy var と決定的に異なるところ
初期化のタイミングを 想定できる!
特徴
初期化タイミングを制御できるなら
ImplicitlyUnwrappedOptional
lazy var ×
ImplicitlyUnwrappedOptional
lazy var × ImplicitlyUnwrappedOptional
▶ 初期化を参照時まで遅らせる ▶ 初期値を決めておける
lazy var
▶ 使うときに値が入っていることを約束する ▶ 値がないことを表現できる
ImplicitlyUnwrappedOptional
性質
ゾンビっぽい変数が生まれる
😱
※ ただしプロパティでの使用に限る
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値を消しても復活する変数
画像キャッシュの機能などに
応用できるかもしれない
lazy
超かっこいい … 😆
lazy var
▶ 初期化されるタイミングが読めない ▶ 読み書きともに mutating 扱い ▶ 複数スレッドで衝突する可能性 ▶ 初期化式とは無関係に値を入れられる
注意したいところ
▶ 単に “便利だから” という理由で使わない ▶ 初期化を遅らせたいだけなら
ImplicitlyUnwrappedOptional も検討したい
魔力にご用心lazy の
まとめlazy var の特徴を知る
1. HIPSTER SWIFT
2. Lazy Variables の動き
3. 大域変数も lazy 扱い
4. ImplicitlyUnwrappedOptional は動きが lazy var と似ている
5. lazy var × ImplicitlyUnwrappedOptional で何度だって蘇る変数が生まれる
6. lazy の魔力にご用心