I have met quite weird case once I used a property with lazy
keyword. I know this keyword indicates that an initialization of the property will be deferred until the variable is actually being used. But, It didn't work as I expected. It run twice.
class TestLazyViewController: UIViewController {
var name: String = "" {
didSet {
NSLog("name self = \(self)")
testLabel.text = name
}
}
lazy var testLabel: UILabel = {
NSLog("testLabel self = \(self)")
let label = UILabel()
label.text = "hello"
self.view.addSubview(label)
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
testLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
NSLayoutConstraint.activateConstraints([NSLayoutConstraint(item: testLabel, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1.0, constant: 0.0)])
NSLayoutConstraint.activateConstraints([NSLayoutConstraint(item: testLabel, attribute: .CenterY, relatedBy: .Equal, toItem: self.view, attribute: .CenterY, multiplier: 1.0, constant: 0.0)])
}
@IBAction func testButton(sender: AnyObject) {
testLabel.text = "world"
}
}
I wrote a view controller for test. This view controller is presented by another view controller. Then, name
property is set in prepareForSegue
of the presenting view controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let vc = segue.destinationViewController as! TestLazyViewController
println("vc = \(vc)")
vc.name = "hello"
}
On running the test, I got the following result.
vc = <testLazy.TestLazyViewController: 0x7fb3d1d16ec0>
2015-05-25 00:26:15.673 testLazy[95577:22267122] name self = <testLazy.TestLazyViewController: 0x7fb3d1d16ec0>
2015-05-25 00:26:15.673 testLazy[95577:22267122] testLabel self = <testLazy.TestLazyViewController: 0x7fb3d1d16ec0>
2015-05-25 00:26:15.674 testLazy[95577:22267122] testLabel self = <testLazy.TestLazyViewController: 0x7fb3d1d16ec0>
As you can see, an initialization code got executed twice. I don't know if it was either a bug or a result from misusing. Is there anybody who can let me know what was wrong?
I am guessing that referencing testLabel
with self.view
in the initialization code is incorrect.
UPDATE:
I still don't understand why lazy initialization runs twice. Is it really Swift's bug?
FINAL UPDATE:
@matt has made an excellent explanation for this problem being initialized twice. I was able to get a valuable knowledge of how lazy
keyword works. Thanks matt.
view
too soon. Do not add major side effects to a mere initializer! Just make the label and return it. Add it as a subview inviewDidLoad
. – Arthritislazy
have to work against this flow. anyway, I will post it to Apple. Thank you. – Gregview
inside lazy block. Do it inviewDidLoad
instead – Kutch