I am probably not the only one who's first thought was to do the following:
class ClassWithLazyProperty {
var a: Int {
set { distance = nil }
get { return self.a }
}
var b: Int {
set { distance = nil }
get { return self.b }
}
lazy var distance: Int? = {
return self.b - self.a
}()
}
While this compiles nicely, it does not work. After the first update to either 'a' or 'b' the distance will be 'nil' and the closure that calculates its value will never be called again.
So I needed something else. This was my second approach:
class ClassWithLazyProperty {
var a: Int {
set { p_distance = nil }
get { return self.a }
}
var b: Int {
set { p_distance = nil }
get { return self.b }
}
var distance: Int {
if p_distance == nil { p_distance = self.b - self.a }
return p_distance!
}
private var p_distance: Int?
}
This actually works, but the code is horrible, and I have to do it again and again for each property that needs this kind of access.
Absent the 'cached' attribute, we can still roll our own. Using generics and closures this looks actually pretty easy. My first approach was this:
class Cached<T> {
private var cached: T?
private var function: () -> T
init(function: () -> T ) {
self.function = function
}
func get() -> T {
if cached == nil {
cached = function()
}
return cached!
}
func reset() {
cached = nil
}
}
And when we need a cached variable:
class ClassWithCachedProperty {
var a: Int { didSet { distance.reset() } }
var b: Int { didSet { distance.reset() } }
var distance = Cached<Int>(function: { [unowned self] in return self.b - self.a } )
init() {
a = 10
b = 100
}
func doSomething() {
println(distance.get())
}
}
Unfortunately that does not compile. The compiler will insist that I use self before it is known. The solution would be to shove the initialisation of the Cached variable into the init function. But then the compiler complains that 'distance' is used before it is initialised.
Fortunately the second error message can be easily avoided:
class Cached<T> {
private var cached: T?
private var function: () -> T
init(function: () -> T ) {
self.function = function
}
func get() -> T {
if cached == nil {
cached = function()
}
return cached!
}
func reset() {
cached = nil
}
}
class ClassWithCachedProperty {
var a: Int { didSet { distance.reset() } }
var b: Int { didSet { distance.reset() } }
var distance: Cached<Int>!
init() {
a = 10
b = 100
distance = Cached(function: { [unowned self] in return self.b - self.a } )
}
func doSomething() {
println(distance.get())
}
}
When we forget to put the proper initialisation in 'init' the thing will bomb on us during runtime. Not a perfect solution, but that possible bomb is easy to find during unit testing, so that is acceptable to me.
Happy coding
Edit: I have refined this approach in this post
Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH
We don't get the world we wish for... we get the world we pay for.
Edit: I have refined this approach in this post
Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH
We don't get the world we wish for... we get the world we pay for.
No comments:
Post a Comment