Overriding methods in Swift extensions Ask Question

Overriding methods in Swift extensions Ask Question

I tend to only put the necessities (stored properties, initializers) into my class definitions and move everything else into their own extension, kind of like an extension per logical block that I would group with // MARK: as well.

For a UIView subclass for example, I would end up with an extension for layout-related stuff, one for subscribing and handling events and so forth. In these extensions, I inevitably have to override some UIKit methods, e.g. layoutSubviews. I never noticed any issues with this approach -- until today.

Take this class hierarchy for example:

public class C: NSObject {
    public func method() { print("C") }
}

public class B: C {
}
extension B {
    override public func method() { print("B") }
}

public class A: B {
}
extension A {
    override public func method() { print("A") }
}

(A() as A).method()
(A() as B).method()
(A() as C).method()

The output is A B C. That makes little sense to me. I read about Protocol Extensions being statically dispatched, but this ain't a protocol. This is a regular class, and I expect method calls to be dynamically dispatched at runtime. Clearly the call on C should at least be dynamically dispatched and produce C?

If I remove the inheritance from NSObject and make C a root class, the compiler complains saying declarations in extensions cannot override yet, which I read about already. But how does having NSObject as a root class change things?

Moving both overrides into their class declaration produces A A A as expected, moving only B's produces A B B, moving only A's produces C B C, the last of which makes absolutely no sense to me: not even the one statically typed to A produces the A-output any more!

Adding the dynamic keyword to the definition or an override does seem to give me the desired behavior 'from that point in the class hierarchy downwards'...

例をもう少し単純なものに変えてみましょう。それが私がこの質問を投稿した本当の理由です。

public class B: UIView {
}
extension B {
    override public func layoutSubviews() { print("B") }
}

public class A: B {
}
extension A {
    override public func layoutSubviews() { print("A") }
}


(A() as A).layoutSubviews()
(A() as B).layoutSubviews()
(A() as UIView).layoutSubviews()

これで、 が得られますA B A。ここでは、UIView の layoutSubviews をどうしても動的にすることはできません。

両方のオーバーライドをクラス宣言に移動するとA A A、再び A のみ、または B のみを取得するとA B Adynamic再び問題が解決します。

dynamic理論的には、これまでに行ったすべての操作に追加できるのですoverrideが、ここでは何か他の間違いをしているような気がします。

extension私のようにコードをグループ化するために s を使用するのは本当に間違っているのでしょうか?

ベストアンサー1

拡張機能は上書きできません/上書きすべきではありません。

Apple の Swift ガイドに記載されているように、拡張機能内の機能 (プロパティやメソッドなど) をオーバーライドすることはできません。

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

Swift 開発者ガイド

コンパイラは、Objective-C との互換性のために拡張機能をオーバーライドすることを許可しています。しかし、これは実際には言語ディレクティブに違反しています。

アイザック・アシモフの「ロボット工学の3つの原則「��

拡張機能(構文糖) は、独自の引数を受け取る独立したメソッドを定義します。つまり、呼び出される関数は、layoutSubviewsコードがコンパイルされるときにコンパイラが知っているコンテキストに依存します。UIView は UIResponder を継承し、UIResponder は NSObject を継承します。拡張機能のオーバーライドは許可されていますが、

したがって、グループ化に問題はありませんが、拡張機能ではなくクラスでオーバーライドする必要があります。

指令ノート

overrideメソッドが Objective-C と互換性がある場合、スーパークラス メソッド、つまりload() initialize()サブクラスの拡張のみが可能です。

したがって、 を使用してコンパイルできる理由を見てみましょうlayoutSubviews

Swift のみのランタイムを許可する純粋な Swift のみのフレームワークを使用する場合を除き、すべての Swift アプリは Objective-C ランタイム内で実行されます。

ご存知のとおり、Objective-C ランタイムは通常、アプリのプロセスでクラスを初期化するときに 2 つのクラス メイン メソッドを自動的にload()呼び出します。initialize()

dynamic修飾語について

からApple 開発者ライブラリ (archive.org)

dynamic修飾子を使用すると、メンバーへのアクセスが Objective-C ランタイムを通じて動的にディスパッチされることを要求できます。

Swift API が Objective-C ランタイムによってインポートされる場合、プロパティ、メソッド、サブスクリプト、または初期化子の動的ディスパッチは保証されません。Swift コンパイラは、Objective-C ランタイムをバイパスして、コードのパフォーマンスを最適化するために、メンバー アクセスを非仮想化またはインライン化する場合があります。��

これは Objective-C で表現されており、そのメンバーへのアクセスは常に Objective-C ランタイムを使用して使用されるため、->dynamicに適用できます。layoutSubviewsUIView Class

そのため、コンパイラではoverrideとの使用が許可されますdynamic

おすすめ記事