Jumhyn
Jumhyn

Reputation: 6777

Passing instance method as callback in class initializer

Consider the following code:

class Bar {
    let callback: () -> ()

    init(callback: @escaping () -> ()) {
        self.callback = callback
    }

    func event() {
        self.callback()
    }
}

class Foo {
    let bar: Bar

    init() {
        self.bar = Bar(callback: self.handler)
    }

    func handler() {
        print("Handled")
    }
}

The basic idea is that we want each Foo to have a Bar, which, when event() is called on a Foo's bar, will call Foo's handler method. However, the setup above warns on Foo's init since we're using self before all instance properties have been initialized.

I understand why this is an error: the compiler cannot guarantee that the method passed to Bar's initializer won't be called somehow before Foo's initializer finishes (e.g. Bar may call the callback param right in it's initializer, or another thread may call event() just after Bar's initializer returns but just before the result is assigned to Foo's bar).

My question is, is there is a way to implement this pattern that I'm missing? One workaround is to make bar an IUO var and assign it to nil just before calling Bar's init--as long as we know the callback won't be called at an improper time (or as long as we don't use bar in handler) there shouldn't be an issue. However, this feels pretty hacky to me and I'd love to hear alternatives if they exist.

Upvotes: 1

Views: 346

Answers (1)

Daniel T.
Daniel T.

Reputation: 33967

Here's another option:

class Foo {
    let bar: Bar

    init() {
        self.bar = Bar(callback: self.handler)
    }

    let handler: () -> Void = {
        print("handled")
    }
}

Maybe it doesn't feel quite as hacky, but if handler deals with self at all, this won't work. You can't do it with a Delegate either.

Yea, AFAIK, the only way to do this is to make bar an implicitly unwrapped var

class Foo {
    var bar: Bar!

    init() {
        bar = Bar(callback: handler)
    }

    func handler() {
        print("Handled")
    }
}

Upvotes: 4

Related Questions