【发布时间】:2015-08-06 04:17:27
【问题描述】:
当我使用带有lazy 关键字的属性时,我遇到了非常奇怪的情况。我知道这个关键字表示属性的初始化将被推迟到变量被实际使用。但是,它并没有像我预期的那样工作。它运行了两次。
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"
}
}
我为测试编写了一个视图控制器。此视图控制器由另一个视图控制器呈现。然后,在呈现视图控制器的prepareForSegue 中设置name 属性。
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let vc = segue.destinationViewController as! TestLazyViewController
println("vc = \(vc)")
vc.name = "hello"
}
在运行测试时,我得到了以下结果。
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>
如您所见,初始化代码执行了两次。我不知道这是一个错误还是滥用的结果。有没有人可以告诉我出了什么问题?
我猜在初始化代码中用self.view 引用testLabel 是不正确的。
更新:
我仍然不明白为什么延迟初始化会运行两次。真的是 Swift 的 bug 吗?
最终更新:
@matt 对这个被初始化两次的问题做了很好的解释。我能够获得有关 lazy 关键字如何工作的宝贵知识。谢谢马特。
【问题讨论】:
-
“我也有猜测,初始化代码中给self.view添加了什么testLabel是不正确的”是不正确的。它会造成可怕的副作用,因为您创建
view太快了。不要将主要的副作用添加到单纯的初始化程序中!只需制作标签并将其退回。将其添加为viewDidLoad中的子视图。 -
我已经准备好提供更多信息,但我仍然不知道是否将其称为 Swift 的错误或您的错误。让我将其添加到我的答案中。
-
好的,一切就绪。我相信我已经完全解释了这个问题。我还没有决定是否称它为 Swift 错误。这当然是一个边缘案例,但它是一个你一开始就不应该接近的边缘。 :) 尽管如此,您还是有理由将测试项目作为错误报告提交给 Apple;他们可能想知道这件事。
-
太棒了!你的解释是最合理的。我无法捕捉到放置在调用堆栈中的点。这显然是递归产生了意想不到的流动。我不确定 Swift
lazy是否必须反对这个流程。无论如何,我会将其发布到Apple。谢谢。 -
简短的回答是我们应该从不在惰性块中访问
view。改为在viewDidLoad中进行
标签: ios swift lazy-initialization