I have a question in regards to how I would do a lazy initialization of a class variable that holds a function. In the project I am working on - in a view controller - I need to run a function based on information that I will only know after the view controller is created. Hence, I wanted to use a lazy initialization to solve this problem. I suppose I could solve the problem other ways, but now I am curious about what I am not understanding about lazy initialization that I am have such difficulty in figuring out how to get a lazily initialized variable to hold a function. That is, if it can be done at all.
Here is some example code of what I am trying to do. I want to be able to call talk()
on an instance of TestClass
and then this instance (tc
in this case) call f()
which is either foo
or bar
depending on circumstances.
class TestClass {
func foo() {
println("foo")
}
func bar() {
println("bar")
}
lazy var f: ()->() = {
return foo
}()
func talk() {
f()
}
}
var tc = TestClass()
tc.f()
Now, this doesn't compile and I get the error:
Playground execution failed: swiftTesting.playground:28:30: error: 'TestClass -> () -> ()' is not convertible to '() -> ()'
lazy var f: ()->() = {
Then I tried changing my f
lazy var to:
lazy var f: TestClass->()->() = {
return foo
}()
This again did not work and now I get the error, Missing argument for parameter #1 in call
.
Can anyone shed some light on how to do lazy initialization with a variable containing a function? I know that I could work around this by not using lazy initialization but I am hoping that someone can help me understand what I am doing wrong in this situation. Thanks.
foo
is an instance method, but closures avoid implicit capture of self
by not automatically binding to it. You need to explicitly say self.foo
.
lazy var f: ()->() = {
return self.foo
}()
tc.f()
// tc is leaked, see below
When you instead used TestClass->()->()
, the method is not bound to anything, so you need to give it an instance.
lazy var f: TestClass->()->() = {
return foo
}()
tc.f(tc)() // Call f with tc to give it an instance, then call the result
The first choice looks ok, but it will actually prevent tc
from being deallocated. Binding foo
to self
and storing it inside self
causes a reference cycle. To avoid this, you need to use a closure with weak capture of self.
lazy var f: ()->() = {
[weak self] in
self!.foo() // ! because self is optional due to being weak
} // no () here
tc.f()
// tc can be deallocated