2

I have a protocol with a method. I have thought that methods can be replaced with closures by the same name, but it doesn't seem to work:

protocol Foo {
    func bar() // Type:  Void -> Void
}

class X: Foo {
    func bar() { }
}

class Y: Foo { // Compiler: doesn't conform to protocol Foo
    let bar: Void->Void = {}
}

Is there a way to make this work? I want to override the methods behavior for a Test Stub implementation. Currently, I'd have to do this, which I'd like to shorten:

class Z: Foo {
    var barClosure: Void -> Void = {}

    func bar() {
        barClosure()
    }
}

let baz = Z()
baz.barClosure = { /* ... */ }
baz.bar() // Calls the closure replacement
ctietze
  • 2,805
  • 25
  • 46

3 Answers3

2

You declared the protocol to have a function, bar(), but in class Y, you just have a constant instead of a function, this is the problem. But if you want to have something like in class Y, you should change the protocol to:

protocol Foo {
    var bar: () -> () {get set}
}

And implement like that:

class Test: Foo {
    private var _bar: (() -> ())?

    var bar: () -> () {
        get {
            return {}
        }
        set {
            self._bar = newValue
        }
    }
}

UPDATED

If you to shorten your class, you can use something like that:

protocol Foo {
    var barClosure: Void -> Void {get set}
}

class Z: Foo {
    var barClosure: Void -> Void = {
        //do here something
    }
}

let a = Z()
a.barClosure()
Dániel Nagy
  • 11,815
  • 9
  • 50
  • 58
  • So ` func` and closures are never interchangeable. I have to decide what it should look like in the class definition. From a client's perspective, both are called the same way. Why did you provide `_bar` additionally when `bar` itself is set-able? – ctietze Feb 13 '15 at 11:23
  • 1
    I would say func and vaiables are not interchangable. I had tha variable _bar to avoid infinite loops. (see this: http://stackoverflow.com/questions/25828632/swift-custom-setter-on-property ) But I will update my answer in order to provide a shorter solution to you problem. – Dániel Nagy Feb 13 '15 at 11:31
2

Thanks to @Dániel Nagy, I was able to figure out what options I have. The protocol should require a closure. This way, the client code won't change, as closure calls are identical to method calls.

  • make the property mutable so implementations can decide if they want to lock the value
  • require a getter only (for the same reason)
  • initialize the property as immutable (let) in production code
  • initialize the property as mutable (var) in test code to provide alternate implementations in test cases, like mock observers do

Here's a modified example which works well in a Playground by returning strings:

protocol Foo {
    var bar: () -> String { get }
}

class X: Foo {
    // cannot be overwritten
    let bar: () -> String = { return "default x" }
}

class Y: Foo {
    private let _bar: () -> String = { return "default y" }

    // Can be overwritten but doesn't have any effect
    var bar: () -> String {
        get {
            return _bar
        }
        set {
        }
    }
}

class Z: Foo {
    // Can be overwidden
    var bar: () -> String = {
        return "default z"
    }
}

let bax = X()
bax.bar() // => "default x"
// bax.bar = { /* ... */ } // Forbidden

let bay = Y()
bay.bar() // => "default y"
bay.bar = { return "YY" }
bay.bar() // => "default y"

let baz = Z()
baz.bar() // => "default z"
baz.bar = { return "ZZ" }
baz.bar() // => "ZZ"
ctietze
  • 2,805
  • 25
  • 46
2

The func keyword does a little bit more magic behind the scenes that you can’t replicate with properties – especially in the case of classes, where functions can be overridden, so vtables need to be built etc.

That said, if you were going to replace methods using closure expressions, you’d need to do more than the code you gave. The equivalent of this:

struct A {
    let x: Int

    func f() {
        println("In f(), x is \(a.x)")
    }
}

would be more like this:

struct A {
    let x: Int

    // returns a function that takes A objects, and 
    // returns a function that captures them
    static let f: (A)->()->() = { a in 
      { ()->() in println("In f(), x is \(a.x)") } 
    }

    // equivalent of the dot notation call of f
    var f: ()->() {
        return A.f(self)
    }
}

This replicates how struct methods actually work, and allows you to do all the same things an f method does:

let a = A(x: 5)

// static version of f
let A_f = A.f

// that static version bound to a self:
let f = A_f(a)
f()

// the above is equivalent to:
a.f()

But this still isn’t enough for A to conform to a protocol that requires an f() method.

Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • This boils down to they aren't the same and can't be made interchangeable. On the downside, it seems using `func` will result in some kind of different optimization from the compiler than using a closure attribute, did I get this right? Would it be a bad idea to declare everything as closure attributes instead of methods? (By "how much" would this be worse?) – ctietze Feb 13 '15 at 11:41