简体   繁体   中英

Lazy initialized variable holding a function swift

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

You need to explictly state self :

lazy var f: ()->() = {
    return self.foo
}()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM