user2727195
user2727195

Reputation: 7330

pass direct reference or closure

Coming from a different language background where passing a method to another object was done differently as demonstrated below (approach A) but I've noticed a style of passing blocks around in Objective-C as well as SWIFT, what's the recommended way in SWIFT and why? (pros and cons any?)

class Messenger {
    private var _method: ((String) -> ())

    init(method: (String -> ())) {
        _method = method
    }
}

class Test {
    init() {
        Messenger(method: self.callback) //approach A

        Messenger({
            message in self.callback(message) //approach B - Wrapper/Block style
        })
    }

    func callback(message: String) {
        println(message)
    }
}

Upvotes: 2

Views: 866

Answers (2)

Airspeed Velocity
Airspeed Velocity

Reputation: 40965

Your method A is the way to go. Method B really doesn’t serve a purpose – you are declaring an anonymous function which takes a single parameter and does nothing to it but use it as an argument to another function, callback. It’s essentially like saying this:

func f(arg1: Int) {
    // do nothing but call g
    g(arg1)
}
f(1)        // no different to calling g(1)
takeFunc(f) // no different to passing in g

It’s important to realize that a function declared with the closure expression syntax, let f: Int->Bool = { i in etc }, and a function declared with the func keyword, func f(i: Int)->Bool { etc } amount to much the same thing. There are subtle differences but not fundamental ones.

Re your concerns about strong references – the closure expression in option B doesn’t really help the situation. In option A you pass an implicit reference to self by passing in a pointer to an instance's member function. In Option B, you declare an anonymous closure that “captures” self, and then pass that, but that anonymous function being stored will keep self alive in just the same way storing self.memfun will, just with an extra level of indirection. In fact, it’s so easy to forget that capturing self via a closure expression can be a risk that Swift will warn you if you do it implicitly by accident and force you to put an explicit self in there (which can be super-annoying sometimes when you know the capture isn’t an issue e.g. when referencing self in a call to map or filter).

To write a callback function that didn’t capture self, you would have to use a capture list:

    Messenger { [weak self] message in // weak or unowned depending on how Messenger is being used 
        self?.callback(message)  // if weak, self needs to be optional
        return // return needed since otherwise it will attempt to return ()?
    }

By the way, the best way to write more Swift-y Swift would be to ditch a lot of the excess brackets :-)

e.g.

// no need to wrap entire function type in (),
// and single argument types (i.e. 1-element tuples)
// don’t need () surrounding them: 
private var _method: String -> ()

instead of

private var _method: ((String) -> ())

and

Messenger { // closure exprs can be outside the () of functions 
            // when they’re the last argument (and () can be 
            // left out completely when they’re the only argument) 
    message in self.callback(message)
}

instead of

Messenger({
        message in self.callback(message) //approach B - Wrapper/Block style
})

Upvotes: 3

Drux
Drux

Reputation: 12660

Your approach B will allow you to insert additional statements between message in and self.callback(message). Without such additional statements (as in your verbatim example) I would favor approach A.

Upvotes: 0

Related Questions