2015-02-13 5 views
1

У меня есть протокол с методом. Я подумал, что методы могут быть заменены затворами одним и тем же именем, но это не похоже на работу:Удовлетворительные методы протокола Swift с атрибутами закрытия?

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 = {} 
} 

Есть ли способ, чтобы сделать эту работу? Я хочу переопределить поведение методов для реализации Test Stub. В настоящее время, я должен был бы сделать это, что я хотел бы сократить:

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

    func bar() { 
     barClosure() 
    } 
} 

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

ответ

2

Благодаря @ Dániel Nagy, я смог выяснить, какие у меня варианты. Протокол должен требовать закрытия. Таким образом, клиентский код не изменится, поскольку вызовы закрытия идентичны вызовам метода.

  • сделать свойство изменяемым так реализации могут решить, если они хотят, чтобы зафиксировать значение
  • требуют поглотителя только (по той же причине)
  • инициализации свойства, как неизменное (let) в производстве кода
  • инициализации свойства, как изменяемые (var) в тестовом коде, чтобы обеспечить альтернативные реализации в тестовых случаях, как фиктивные наблюдатели делают

Вот мо dified пример, который хорошо работает в Playground возвращающиеся строк:

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" 
2

Вы объявили протокол, чтобы иметь функцию, bar(), но в классе Y, вы просто константа вместо функции , это проблема. Но если вы хотите иметь что-то, как в классе Y, вы должны изменить протокол к:

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

И реализовать так:

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

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

ОБНОВЛЕНО

Если вы сократить ваши класс, вы можете использовать что-то вроде этого:

+0

Таким образом, 'func' и замыкания никогда не являются взаимозаменяемыми. Я должен решить, как это должно выглядеть в определении класса. С точки зрения клиента, оба они называются одинаково. Почему вы предоставили '_bar' дополнительно, когда' bar' сам настроен? – ctietze

+1

Я бы сказал, что func и vaiables не взаимозаменяемы. У меня была переменная _bar, чтобы избежать бесконечных циклов. (см. это: http://stackoverflow.com/questions/25828632/swift-custom-setter-on-property) Но я обновлю свой ответ, чтобы предоставить вам более короткое решение проблемы. –

+0

@ctietze приветствуется! –

2

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

+0

Это сводится к тому, что они не то же самое и не могут быть взаимозаменяемыми. С другой стороны, кажется, что использование 'func' приведет к какой-то другой оптимизации от компилятора, чем к атрибуту закрытия, правильно ли я это понял? Было бы плохой идеей объявить все как атрибуты закрытия вместо методов? («Насколько» это будет хуже?) – ctietze

Смежные вопросы