Is it safer to use computed property backed by another property, than to use lazy
modifier in Swift?
// method 1: using LAZY variable
lazy var myVar:Int {
// assume that this calculation takes
// a lot of time and resources
return 5
}
Now consider the following quote from Apple's Swift 2 (Pre-release):
NOTE
If a property marked with the
lazy
modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.
Personally, I have been doing my own "lazy" logic like this:
Consider the alternative approach:
// method 2: using calculated property backed by another property
var myVar:Int? {
if self._myVar != nil {
return self._myVar
}
let aVar:Int = 5 // assume this is done after exhaustive calcuation
self._myVar = aVar
return self._myVar
}
var _myVar:Int? = nil
Using (method 2), do I get guarantee that the "exhaustive calculation" will only be executed once?
Your proposed method 2 does not guarantee anything that lazy
does not.
// method 2: using calculated property backed by another property var myVar:Int? { if self._myVar != nil { return self._myVar } let aVar:Int = 5 // assume this is done after exhaustive calcuation self._myVar = aVar return self._myVar } var _myVar:Int? = nil
Moreover, it has the added pitfall of being an optional rather than a non-optional. This would be slightly better as an implicitly unwrapped optional so we don't have to continuously unwrap it.
And, we can't set myVar
. Plus _myVar
should be private.
But the problem is that you're not locking the initialization code down to be synchronized.
Suppose you have thread A start to access myVar
. The if self._myVar != nil
check returns false, so we're not returning self._myVar
. Now we enter the exhaustive calculation.
Now before exhaustive calculation completes, Thread B tries accessing myVar
. Because Thread A has not completed exhaustive calculation yet, the if self._myVar != nil
still returns false, and now Thread B does the exhaustive calculation.
Essentially, this is this the same problem that the lazy
keyword gives you.... but you wrote a whole lot of code to do it that way when lazy
would have sufficed.