满足具有闭包属性的Swift协议方法?

满足具有闭包属性的Swift协议方法?,swift,closures,protocols,Swift,Closures,Protocols,我有一个带有方法的协议。我认为方法可以用同名的闭包代替,但它似乎不起作用: 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 = {} } 有没有办法让这一切顺利进行?我想覆盖测试存

我有一个带有方法的协议。我认为方法可以用同名的闭包代替,但它似乎不起作用:

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 = {}
}
有没有办法让这一切顺利进行?我想覆盖测试存根实现的方法行为。目前,我必须这样做,我想缩短:

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

    func bar() {
        barClosure()
    }
}

let baz = Z()
baz.barClosure = { /* ... */ }
baz.bar() // Calls the closure replacement

您声明协议有一个函数,
bar()
,但在类
Y
中,您只有一个常量而不是一个函数,这就是问题所在。但是,如果您想在类
Y
中使用类似的内容,则应将协议更改为:

protocol Foo {
    var bar: () -> () {get set}
}
并实施如下:

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

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

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

let a = Z()
a.barClosure()
已更新

如果你想缩短你的课程,你可以使用这样的方法:

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

    var bar: () -> () {
        get {
            return {}
        }
        set {
            self._bar = newValue
        }
    }
}
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,我才知道我有什么选择。该协议应要求关闭。这样,客户端代码就不会更改,因为闭包调用与方法调用是相同的

  • 使属性可变,以便实现可以决定是否要锁定该值
  • 仅需要一个getter(出于相同的原因)
  • 在生产代码中将属性初始化为不可变(
    let
  • 在测试代码中将属性初始化为可变(
    var
    )以在测试用例中提供替代实现,就像模拟观察者一样
下面是一个经过修改的示例,它通过返回字符串在操场中运行良好:

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"

func
关键字在幕后发挥了一些你无法用属性复制的魔力——特别是在类的情况下,在类中函数可以被重写,因此需要构建vtables等等

这就是说,如果要使用闭包表达式替换方法,则需要做的工作比提供的代码更多。相当于:

struct A {
    let x: Int

    func f() {
        println("In f(), x is \(a.x)")
    }
}
更像这样:

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)
    }
}
这复制了struct方法的实际工作方式,并允许您执行与
f
方法相同的所有操作:

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()

但这仍然不足以使
A
遵守需要
f()
方法的协议。

因此'func'和闭包永远不能互换。我必须决定它在类定义中应该是什么样子。从客户的角度来看,两者都被称为相同的方式。当
bar
本身设置为可交换时,为什么还要另外提供
\u bar
?我想说func和vaiables是不可交换的。我有一个变量条来避免无限循环。(见此:)但我会更新我的答案,以便为您的问题提供一个较短的解决方案。这归结为它们不同,不能互换。另一方面,使用
func
似乎会导致编译器进行与使用闭包属性不同的优化,我做对了吗?将所有内容声明为闭包属性而不是方法是一个坏主意吗?(通过“多少”这会更糟吗?)