Edited at

【Swift4.1】lazyプロパティの使い所

More than 1 year has passed since last update.


はじめに

Swiftを学び始めてしばらく経ちますが、

lazyプロパティをまだ上手く使いこなせていません。

そこで、具体的にどんなところで使うことが出来るのかをまとめてみました。


基礎

参照された時に初めて初期値が設定されるプロパティ


lazyなし

var defaultPrice = 100

class Item {
var price: Int = defaultPrice
}

let item = Item()

defaultPrice = 200
print(item.price) // 100



lazyあり

var defaultPrice = 100

class Item {
lazy var price: Int = defaultPrice
}

let item = Item()

defaultPrice = 200
print(item.price) // 200

defaultPrice = 300
print(item.price) // 200


ImplicitlyUnwrappedOptionalに似ているが、

lazyでは宣言時に値の初期値を決めておくことが出来る。


初期化タイミングに合わせて初期値を決定したい時

初期化時点で計算メソッドを呼び出したい時に使える。

該当プロパティのみのためであればクロージャでも代用可能。

var defaultPrice: Double = 100

class Item {
// lazyがないとコンパイルエラー
lazy var price: Double = applyTax(price: defaultPrice)

func applyTax(price: Double) -> Double {
return price * 1.08
}
}

let item = Item()

print(item.price) // 108


複数のイニシャライザで呼び出していた処理を一箇所にしたい時

今まで適当に初期化してイニシャライザで設定していたのをまとめられる。

class CustomView: UIView {

// Before : var subView = SubView()
lazy var subView: SubView = setupSubView()

override init(frame: CGRect) {
super.init(frame: frame)
// Before : 初期化時にSubView()してViewの設定をしていたメソッド
// Before : setupSubView()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Before : 初期化時にSubView()してViewの設定をしていたメソッド
// Before : setupSubView()
}

private func setupSubView() -> SubView {
let view = SubView()
view.frame = CGRect(...)
return view
}
}


特定のオブジェクトを生成する静的なメソッドを特定のタイミングでのみ呼び出したい時

ファクトリーメソッドを初期化時点で記述出来る。

class ViewController: UIViewController {

lazy var actionButton: UIButton = ViewFactory.makeActionButton()

func present(infoView: InfoView) {
view.addSubview(infoView)
infoView.frame = view.bounds.offsetBy(dx: 0, dy: view.bounds.height)
infoView.present(animated: true)

// ここで初めてActionButtonが生成される
actionButton.center = view.center
view.addSubview(actionButton)
}
}


非オプショナルを求めるプロトコルのプロパティに後から値を入れたい時

参照時には値が初期化されるため、非オプショナルに対応出来る。

protocol ItemProtocol {

var price: Double { get }
}

class Item: ItemProtocol {
lazy var price: Double = getPrice()

func getPrice() -> Double {
return 100.0
}
}

let item = Item()

print(item.price) // 100


注意

便利なlazyですが、

初期化タイミングが不明瞭なため思わぬバグを引き起こすことがあります。

私見ではありますが、

プロジェクト内での利用タイミングの統一やコメントでの補足等しておく必要はあるかと思います。


参考

http://hajihaji-lemon.com/smartphone/swift/lazy/

https://medium.com/@johnsundell/using-lazy-properties-in-swift-592c777e0052

http://www.ssktm.info/entry/2017/11/28/093046

https://www.slideshare.net/tomohirokumagai54/lazy-var-cocoakansai-cswift