2015-08-03 2 views
8

У меня возникла проблема с использованием расширений протокола Swift 2 с реализациями по умолчанию. Основная идея заключается в том, что я предоставил стандартную реализацию метода протокола, который я переопределяю в классе, который реализует протокол. Этот метод расширения протокола вызывается из базового класса, который затем вызывает метод, который я переопределял в производном классе. В результате переопределенный метод не вызывается.Расширение протокола Swift 2 не вызывает переопределенный метод правильно

Я попытался решить проблему до наименьшей возможной площадки, которая иллюстрирует проблему ниже.

protocol CommonTrait: class { 
    func commonBehavior() -> String 
} 

extension CommonTrait { 
    func commonBehavior() -> String { 
     return "from protocol extension" 
    } 
} 

class CommonThing { 
    func say() -> String { 
     return "override this" 
    } 
} 

class ParentClass: CommonThing, CommonTrait { 
    override func say() -> String { 
     return commonBehavior() 
    } 
} 

class AnotherParentClass: CommonThing, CommonTrait { 
    override func say() -> String { 
     return commonBehavior() 
    } 
} 

class ChildClass: ParentClass { 
    override func say() -> String { 
     return super.say() 
     // it works if it calls `commonBehavior` here and not call `super.say()`, but I don't want to do that as there are things in the base class I don't want to have to duplicate here. 
    } 
    func commonBehavior() -> String { 
     return "from child class" 
    } 
} 

let child = ChildClass() 
child.say() // want to see "from child class" but it's "from protocol extension” 
+0

Я рекомендую вам прочитать этот пост здесь, чтобы понять, как это работает: http://nomothetis.svbtle.com/the-ghost-of-swift-bugs-future – DevAndArtist

ответ

7

К сожалению, протоколы не имеют такого динамического поведения (пока).

Но вы можете сделать это (с помощью классов), осуществив commonBehavior() в ParentClass и переопределив его в ChildClass. Кроме того, необходимо CommonThing или другой класс, чтобы соответствовать CommonTrait, который затем суперкласс ParentClass:

class CommonThing: CommonTrait { 
    func say() -> String { 
     return "override this" 
    } 
} 

class ParentClass: CommonThing { 
    func commonBehavior() -> String { 
     // calling the protocol extension indirectly from the superclass 
     return (self as CommonThing).commonBehavior() 
    } 

    override func say() -> String { 
     // if called from ChildClass the overridden function gets called instead 
     return commonBehavior() 
    } 
} 

class AnotherParentClass: CommonThing { 
    override func say() -> String { 
     return commonBehavior() 
    } 
} 

class ChildClass: ParentClass { 
    override func say() -> String { 
     return super.say() 
    } 

    // explicitly override the function 
    override func commonBehavior() -> String { 
     return "from child class" 
    } 
} 
let parent = ParentClass() 
parentClass.say()   // "from protocol extension" 
let child = ChildClass() 
child.say()    // "from child class" 

Поскольку это только короткое решение для вашей проблемы, я надеюсь, что она вписывается в ваш проект.

+0

Еще один хороший способ решить эту проблему более конкретно (не так, как обычно. Только «ChildClass» будет иметь такое поведение), заменив код в функции 'say()' 'ParentClass' на return (self as? ChildClass) ? .commonBehavior() ?? commonBehavior() '(используя пример кода вопроса). – Qbyte

+2

Это значит, что родительский класс должен знать об этом подклассе, правда? Скорее нарушает инкапсуляцию. – Remover

+0

@ Вернитесь, вы правы, суперкласс должен знать что-то о своих подклассах, которые действительно нарушают инкапсуляцию. Поэтому это должен быть только пример того, что еще вы могли бы сделать, хотя я бы не рекомендовал его. – Qbyte

1

Чтобы упростить мое отсутствие этого понимания, что означает слово «все еще» в случае неспецифической ошибки. Я понял, что я не могу написать функцию с аргументами, в переопределении расширенной функции, и компилятор дает мне такую ​​ошибку, но если я напишу простую функцию без аргументов и сделаю пользовательскую реализацию и вызову ее с моей переопределенной «простой функцией». Это работает:

import Foundation 
import UIKit 

extension UIViewController { 

    func slideInView(direction: Direction = Direction.LEFT, duration: CFTimeInterval = 0.5, closure:()->()) { 

     let animation    = CABasicAnimation(keyPath: "transform.translation.x") 
     animation.fromValue   = self.view.bounds.width 
     animation.toValue   = 0 
     animation.duration   = 0.3 
     animation.fillMode   = kCAFillModeForwards; 
     animation.removedOnCompletion = false 

     UIView.animateWithDuration(0.6, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: { 
      self.view!.layer.addAnimation(animation,forKey:nil); 

      }, completion: {(finished) ->() in 
       closure() 
     }); 
    } 

    func slide() { 
     self.slideInView(.LEFT,duration: 0.66) { 
      print("Slide in Left Complete") 
     } 
    } 
} 

class OtherUIViewController: UIViewController { 



    override func slide() { 
     self.slideFromBottom(.BOTTOM,duration: 0.66) { 
      print("Slide in Bottom Complete") 
     } 
    } 

    func slideFromBottom(direction: Direction = Direction.BOTTOM, duration: CFTimeInterval = 0.5, closure:()->()) { 

     let animation    = CABasicAnimation(keyPath: "transform.translation.y") 
     animation.fromValue   = self.view.bounds.height 
     animation.toValue   = 0 
     animation.duration   = 0.3 
     animation.fillMode   = kCAFillModeForwards 
     animation.removedOnCompletion = false 

     UIView.animateWithDuration(0.6, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: { 
      self.view!.layer.addAnimation(animation,forKey:nil); 

      }, completion: {(finished) ->() in 
       closure() 
     }); 
    } 
} 
1

Это поведение Свифта. Это может быть хорошо или плохо, в зависимости от ваших потребностей. Быстрое использование статической отправки, поэтому метод, который вызывается, должен быть известен во время компиляции. Есть некоторые преимущества и, как правило, некоторые недостатки. Чтобы увидеть, как работает Swift в настоящее время, см. Следующий очень простой пример. Для меня это выглядит логично ...

protocol P { 
    func foo()->Void 
} 
extension P { 
    func foo()->Void { 
     print("protocol foo") 
    } 
} 
class A:P { 
} 
class B:A { 
    func foo() { 
     print("B foo") 
    } 
} 
class C:B { 

} 
class D: C { 
    // here the implementation must be overriden, 
    // due the indirect inheritance from B 
    override func foo() { 
     print("D foo") 
    } 
} 
let a = A()  // a is A type 
a.foo()   // protocol foo 
let b = B()  // B is B type 
b.foo()   // B foo 
let p:P = B() // p is protocol P 
// compiler is not able to know, what i would like, the dynamicType of p 
// can be everything, conforming to protocol P 
p.foo()   // protocol foo 
(p as? A)?.foo() // protocol foo 
// casting to B type, I decided, that p is B type 
(p as? B)?.foo() // B foo 
(p as? D)?.foo() // nothing is printed, becase the result of casting is nil 

// here the types are known at compile time 
let c = C() 
c.foo()   // B foo 
let d = D() 
d.foo()   // D foo 
let e:C = D() 
e.foo()   // D foo 
Смежные вопросы