2015-04-27 46 views
14

我有UIView的一个具体子类实现的协议斯威夫特 - 我怎样才能在一个具体子类

protocol SomeProtocol { 
    var property : Int 
} 
    extension UIView : SomeProtocol { 
     var property : Int { 
     get { 
      return 0 
     } 
     set { 
      // do nothing 
     } 
     } 
    } 

延长我想重写此扩展方法重载的扩展方法:

class Subclass : UIView, SomeProtocol { 
    var _property : Int = 1 
    var property : Int { 
    get { return _property} 
    set(val) {_property = val} 
    } 
} 

我设置了断点,并看到扩展方法被调用,而不是具体的子类方法:

var subclassObject = Subclass() 

someObject.doSomethingWithConcreteSubclassObject(subclassObject) 

// other code; 

fun doSomethingWithConcreteSuclassObject(object : UIView) { 
    var value = object.property // always goes to extension class get/set 
} 
+0

我也尝试添加重写,但超类没有此属性 –

+2

Wich Swift版本您使用?在Swift 1.2和Xcode 6.3.1中,当您尝试覆盖_extension_属性时,出现以下编译时错误:“来自扩展的声明不能被覆盖”,并且_yet_暗示他们将来会出现。 –

+0

有趣,所以它看起来像这个功能即将在未来的版本。 –

回答

12

由于others have noted,斯威夫特没有(还)允许您覆盖在一个类扩展声明的方法。然而,即使/有时候Swift允许你重写这些方法,我也不确定你是否会得到你想要的行为。

考虑Swift如何处理协议和协议扩展。给定一个协议来打印一些metasyntactic变量名:

protocol Metasyntactic { 
    func foo() -> String 
    func bar() -> String 
} 

的扩展,提供默认的实现:

extension Metasyntactic { 
    func foo() -> String { 
     return "foo" 
    } 

    func bar() -> String { 
     return "bar" 
    } 
} 

这符合协议的类:

class FooBar : Metasyntactic { 
    func foo() -> String { 
     return "FOO" 
    } 

    func bar() -> String { 
     return "BAR" 
    } 
} 

斯威夫特将使用动态调度调用适当的实现foo()bar() ba sed的每个变量的运行时类型而不是由编译器推断类型:

let a = FooBar() 
a.foo() // Prints "FOO" 
a.bar() // Prints "BAR" 

let b: Metasyntactic = FooBar() 
b.foo() // Prints "FOO" 
b.bar() // Prints "BAR" 

但是,如果我们延长该协议进一步增加了一种新方法:

extension Metasyntactic { 
    func baz() -> String { 
     return "baz" 
    } 
} 

如果我们覆盖我们的新方法,在符合协议的类:

class FooBarBaz : Metasyntactic { 
    func foo() -> String { 
     return "FOO" 
    } 

    func bar() -> String { 
     return "BAR" 
    } 

    func baz() -> String { 
     return "BAZ" 
    } 
} 

斯威夫特将现在使用静态调度调用基于由编译器推断类型的相应实施baz()

let a = FooBarBaz() 
a.baz() // Prints "BAZ" 

let b: Metasyntactic = FooBarBaz() 
b.baz() // Prints "baz" 

亚历山德罗·萨拉萨尔有a fantastic blog post explaining this behavior in depth,但我只想说,斯威夫特只使用在原协议中声明的方法的动态调度,不在协议扩展中声明的方法的。我想,类扩展也是如此。

+0

非常棒的答案,谢谢。 – OhadM

-3

我觉得你忘了重写你的子类中的超类属性:

class Subclass : UIView { 
    var _property : Int = 1 
    override var property : Int { 
     get { return _property} 
     set(val) {_property = val} 
    } 
} 
+0

我也尝试过。它会得到一个语法错误:“属性不会覆盖任何超类的属性” –

+0

这很奇怪。看起来像你的子类不能访问超级的属性。他们是否在同一个源文件中声明?你可能不得不明确宣布公开。 –

+1

看看http://stackoverflow.com/questions/27109006/can-you-override-between-extensions-in-swift-or-not-compiler-seems-confused – Moritz

0

看起来你可以覆盖第2个超类属性的属性。例如,您可以通过扩展UILabel以覆盖frame财产UIView来访问UIView财产。此示例对我的作品在Xcode 6.3.2

extension UILabel { 

    override public var frame: CGRect { 

     didSet { 
      println("\(frame)") 
     } 
    } 
} 
0

我知道这个问题已经问了一段时间了。但是对于寻找更简单方法的人来说,这将非常方便。有一种重写扩展方法的方法。我知道它有点不好意思,但它很好地完成了这项工作。

如果用@objc

@objc protocol MethodOverridable { 
    func overrideMe() 
} 

声明你的协议在扩展

extension MainClass: MethodOverridable { 
    func overrideMe() { 
     print("Something useful") 
    } 
} 

子类 - 你可以能够覆盖它在你的子类。它像一个魔术般运作。那么,当添加@objc时,并不是真的会暴露您的protocol to Objective-C及其Runtime。这可以让你的子类覆盖。

class SubClass: MainClass { 
    override func overrideMe() { 
      print("Something more useful") 
     } 
    }