2016-05-27 16 views
13

エクステンションのメソッドシグネチャをオーバーライドすると、特定のケースで予期しない結果が発生するようです。次の例は、同様のパターンを持つ2つの異なる結果を示しています。サブクラス拡張のオーバーライドされたメソッドへの迅速なディスパッチ

class A: UIViewController { 
    func doThing() { 
     print("dothing super class") 
    } 

    override func viewDidLoad() { 
     print("viewdidload superclass") 
     super.viewDidLoad() 
    } 
} 

class B: A { } 

extension B { 
    override func doThing() { 
     print("dothing sub class") 
     super.doThing() 
    } 

    override func viewDidLoad() { 
     print("viewdidload subclass") 
     super.viewDidLoad() 
    } 
} 

let a: A = B() 
a.doThing() 

let vc: UIViewController = B() 
vc.viewDidLoad() 

これは、出力します。あなたはそれがAとしてキャストされたときに、これはdoThingBの実装をスキップして見ることができます

dothing super class 
viewdidload subclass 
viewdidload superclass 

UIViewControllerとしてキャストするとき、しかしviewDidLoadの両方の実装が含まれています。これは期待される行動ですか?もしそうなら、これはどうしてですか?

ENV:Xcodeの7.3、遊び場

+7

スーパークラスで 'dynamic func doThing()'を試してみてください。 – jtbandes

+0

@jtbandes期待される結果が得られます(つまり、サブクラス実装とスーパークラス実装の両方を呼び出します)。しかし、なぜそれが要求され、 'UIViewController'にキャストされたときの振る舞いと違うのかについて私は明確ではありません。モジュールの範囲に基づいて動作が異なりますか? – ahtierney

答えて

12

ここで驚きは、コンパイラが拡張子でオーバーライドを可能にすることです。このコンパイルしません: - おそらくこのクラスはObjective-Cのと対話することを可能にするために、あなたの例では

class A { 
    func doThing() { 
     print("dothing super class") 
    } 
} 
class B: A { 
} 
extension B { 
    override func doThing() { // error: declarations in extensions cannot override yet 
     print("dothing sub class") 
     super.doThing() 
    } 
} 

、AがNSObjectのから派生しているため、コンパイラはあなたにパスを与えることが表示されます。このコンパイルを行います。

class A : NSObject { 
    func doThing() { 
     print("dothing super class") 
    } 
} 
class B: A { 
} 
extension B { 
    override func doThing() { 
     print("dothing sub class") 
     super.doThing() 
    } 
} 

私の推測では、あなたがすべてで、このオーバーライドを行うことが許されているという事実は、おそらくバグ自体があるということです。 docsは言う:

拡張機能は、型に新しい機能を追加することができますが、彼らは既存の機能をオーバーライドすることはできません。

オーバーライドは、拡張機能が行うことのできるものの1つとして記載されていません。だから、これはでなく、のコンパイルが必要です。しかし、おそらくこれはObjective-Cとの互換性のために意図的に許されています。いずれにしても、我々は端の場合を探求しています。そして、あなたは非常にきれいにその縁起を引き出しています。

特に、前述のコードでは、ダイナミックディスパッチが有効になることはありません。だから、dynamicとして@@tbandesのようにdoThingを宣言したり、多態性を動作させたい場合は、拡張子ではなく実際のクラスに入れなければなりません。したがって、これはあなたが期待どおりに動作します:

class A : NSObject { 
    dynamic func doThing() { 
     print("dothing super class") 
    } 
} 
class B: A { 
} 
extension B { 
    override func doThing() { 
     print("dothing sub class") 
     super.doThing() 
    } 
} 

をので、この行います

class A : NSObject { 
    func doThing() { 
     print("dothing super class") 
    } 
} 
class B: A { 
    override func doThing() { 
     print("dothing sub class") 
     super.doThing() 
    } 
} 

私の結論は次のようになります。非常に素晴らしい例。起こりうるバグとしてAppleに提出してください。そしてしないでください。エクステンションではなく、クラスでオーバーライドしてください。

関連する問題