C++/Java/C#のバックグラウンドから来ると、Swiftで仮想メソッドが表示されることを期待していましたが、Swiftのドキュメントを読むと、仮想メソッドについての言及がありません。
何が足りないのですか?
C++とは異なり、Swiftでメソッドが仮想であることを指定する必要はありません。コンパイラーは、次のうちどれを使用するかを決定します。
(パフォーマンスメトリックはもちろんハードウェアによって異なります)
もちろん、Objective-Cは常に後者を使用します。 4.9nsのオーバーヘッドは、メソッドの実行時間全体のごく一部に相当するため、通常は問題になりません。ただし、必要に応じて、開発者はCまたはC++にシームレスにフォールバックできます。ただし、Swiftでは、コンパイラーは最も高速なものを分析し、ユーザーに代わって決定を試みます。インライン、静的、仮想を優先しますが、Objective-Cの相互運用性のためにメッセージングを保持します。メソッドをdynamic
でマークして、メッセージングを促進することができます。
これの副作用の1つは、動的ディスパッチによって提供される強力な機能の一部が利用できない可能性があることです。これは、以前はすべてのObjective-Cメソッドに当てはまると想定されていたためです。動的ディスパッチはメソッドインターセプトに使用され、メソッドインターセプトは次のユーザーによって使用されます。
上記の種類の機能は、late binding
言語によって提供される機能です。 Javaはメソッド呼び出しにvtableディスパッチを使用しますが、それでも遅延バインディング言語と見なされているため、仮想マシンとクラスローダーシステムを備えているため、上記の機能を利用できます。ランタイムインストルメンテーションを提供するためのアプローチ。 "Pure" Swift(Objective-C相互運用なし)は、静的ディスパッチを備えた直接実行可能なコンパイル済み言語であるという点でC++に似ており、これらの動的機能ARCの伝統では、これらの種類の機能の多くがコンパイル時間に移行するのを目にするかもしれません。これは、モバイルコンピューティングにおける重要な考慮事項である「ワットあたりのパフォーマンス」に関してエッジを与えます。
すべてのメソッドは仮想です。ただし、override
キーワードを使用して、基本クラスのメソッドをオーバーライドすることを宣言する必要があります。
Swiftプログラミングガイド から:
オーバーライド
サブクラスは、インスタンスメソッド、クラスメソッド、インスタンスプロパティ、またはスーパークラスから継承する添え字の独自のカスタム実装を提供できます。これはオーバーライドとして知られています。
継承される特性をオーバーライドするには、オーバーライドする定義の前に
override
キーワードを付けます。そうすることで、オーバーライドを提供するつもりであり、誤って一致する定義を提供していないことが明確になります。誤ってオーバーライドすると、予期しない動作が発生する可能性があり、override
キーワードのないオーバーライドは、コードのコンパイル時にエラーと診断されます。
override
キーワードは、Swiftコンパイラーに、オーバーライドするクラスのスーパークラス(またはその親の1つ)に、オーバーライド用に指定した宣言と一致する宣言があることを確認するように求めます。これチェックは、オーバーライドする定義が正しいことを確認します。
_class A {
func visit(target: Target) {
target.method(self);
}
}
class B: A {}
class C: A {
override func visit(target: Target) {
target.method(self);
}
}
class Target {
func method(argument: A) {
println("A");
}
func method(argument: B) {
println("B");
}
func method(argument: C) {
println("C");
}
}
let t = Target();
let a: A = A();
let ab: A = B();
let b: B = B();
let ac: A = C();
let c: C = C();
a.visit(t);
ab.visit(t);
b.visit(t);
ac.visit(t);
c.visit(t);
_
self
およびA
のvisit()
のC
参照に注意してください。 Javaの場合と同様に、コピーされませんが、代わりにself
は、オーバーライドで再び使用されるまで同じタイプを保持します。
結果はA、A、A、C、Cであるため、動的ディスパッチは利用できません。残念ながら。
Xcode8.x.xおよび9Betaの時点で、C++の仮想メソッドは次のようにSwift 3および4に変換される可能性があります。
protocol Animal: AnyObject { // as a base class in C++; class-only protocol in Swift
func hello()
}
extension Animal { // implementations of the base class
func hello() {
print("Zzz..")
}
}
class Dog: Animal { // derived class with a virtual function in C++
func hello() {
print("Bark!")
}
}
class Cat: Animal { // another derived class with a virtual function in C++
func hello() {
print("Meow!")
}
}
class Snoopy: Animal { // another derived class with no such a function
//
}
試してみる。
func test_A() {
let array = [Dog(), Cat(), Snoopy()] as [Animal]
array.forEach() {
$0.hello()
}
// Bark!
// Meow!
// Zzz..
}
func sayHello<T: Animal>(_ x: T) {
x.hello()
}
func test_B() {
sayHello(Dog())
sayHello(Cat())
sayHello(Snoopy())
// Bark!
// Meow!
// Zzz..
}
要約すると、C++で行う同様のことは、SwiftのProtocolおよびGenericで実現できると思います。
私もC++の世界から来て、同じ質問に直面しました。上記は機能しているように見えますが、C++の方法のように見えますが、多少迅速な方法ではありません。
それ以上の提案は大歓迎です!