View
や ViewController
を作る際に、初期化にあまり時間をかけたくないなどといった理由ですぐに使わないけど Optional でない部品を lazy var
使って遅延生成する手法があります。例えば ViewController
に ChildViewController
を持ちたいけど、実際 ChildViewController
が必要になるまで生成したくないといった場合がまさにこうです。
import UIKit
class ViewController: UIViewController {
lazy var childController = ChildViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
print("Root controller view did load")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("Touches in root controller ended")
self.presentViewController(self.childController, animated: true) {
print("Child controller presented")
}
}
}
import UIKit
class ChildViewController: UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
print("Child controller inited")
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("Child controller view did load")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
この場合、ViewController
にタップするまで、ChildViewController
の部品は生成されないことをログで見れば一目瞭然です。
Root controller view did load
Touches in root controller ended
Child controller inited
Child controller view did load
Child controller presented
ところが、このような単純な lazy var
を使う場合、生成時に色々設定を入れてあげること(backgroundColor
を設定してあげたいとか色々)ができません。
じゃあどうすればいいかというと、少なくとも二通りの方法があります。
まず一つ目は非常にレガシーなやり方で Objective-C の時代からある方法です。もう一つ private
変数を使うことです。例えば上記の lazy var childController = ChildViewController()
ですと、下記のように書きます:
//前略
private var _childController: ChildViewController?
var childController: ChildViewController {
if let controller = self._childController {
return controller
} else {
let controller = ChildViewController()
self._childController = controller
controller.view.backgroundColor = .redColor()
return controller
}
}
//後略
このように、一つ private var _childController
という変数を作ることによって、lazy var
と同じ効果が得られる上、生成時に色々設定をしてあげることも可能です。
もう一つの方法は lazy var
をそのまま使うが、クロージャーも一緒に取り入れるという Swift ならではの方法です:
//前略
lazy var childController = {() -> ChildViewController in
let controller = ChildViewController()
controller.view.backgroundColor = .redColor()
return controller
}()
//後略
両方とも通常の lazy var
の時と全く同じように self.childController
でそのまま使えますが、クロージャーの方が余計なインスタンス変数を作らずに済むし記述量も少ないのでとてもスマートなコードが書けます。ただし一つだけ制限もありまして、コメント情報によりますと、クロージャーの中に lazy var
を使う場合は使われてる時クラスが生成されたことが保証できないため、クロージャーの中に self
が使えません。なので self
を使った設定は private var _
を使うしかなさそうです。self
を使いたい場合は型を明示すればできます:
//前略
lazy var childController: ChildViewController = {
let controller = ChildViewController()
controller.view.backgroundColor = self.view.backgroundColor()
return controller
}()
//後略