web-dev-qa-db-ja.com

Swiftはリフレクションをサポートしていますか?

Swiftはリフレクションをサポートしていますか?例えばSwiftオブジェクトのvalueForKeyPath:setValue:forKeyPath:のようなものはありますか?

実際、Objective-Cのobj.classのような動的型システムさえありますか?

112
Khanh Nguyen

いくつかのリフレクションサポートの開始があるようです。

class Fruit {
    var name="Apple"
}

reflect(Fruit()).count         // 1
reflect(Fruit())[0].0          // "name"
reflect(Fruit())[0].1.summary  // "Apple"

Mchambers Gistから、ここ: https://Gist.github.com/mchambers/fb9da554898dae3e54f2

85
stevex

クラスがNSObjectを拡張する場合、Objective-Cのイントロスペクションとダイナミズムはすべて機能します。これも:

  • メソッドとプロパティについてクラスに問い合わせ、メソッドを呼び出したりプロパティを設定したりする機能。
  • メソッド実装を交換する機能。 (すべてのインスタンスに機能を追加します)。
  • その場で新しいサブクラスを生成して割り当てる機能。 (特定のインスタンスに機能を追加)

この機能の欠点の1つは、Swiftオプション値タイプのサポートです。たとえば、Intプロパティは列挙および変更できますが、Int?プロパティはできません。オプションの型は、reflect/MirrorTypeを使用して部分的に列挙できますが、変更することはできません。

クラスがNSObjectを拡張しない場合、新しい、非常に限定された(進行中の)リフレクションのみが機能します(reflect/MirrorTypeを参照)。これにより、クラスとプロパティについてインスタンスに問い合わせる制限された機能が追加されますが、なし上記の追加機能の。

NSObjectを拡張しない場合、または '@objc'ディレクティブを使用する場合、Swiftはデフォルトで静的およびvtableベースのディスパッチになります。これは高速ですが、仮想マシンがない場合、ランタイムメソッドのインターセプトは許可されません。このインターセプトはCocoaの基本的な部分であり、次のタイプの機能に必要です。

  • ココアのエレガントな物件オブザーバー。 (プロパティオブザーバーはSwift言語に直接ベイク処理されます)。
  • ロギング、トランザクション管理(アスペクト指向プログラミング)などの分野横断的な懸念を非侵襲的に適用します。
  • プロキシ、メッセージ転送など.

したがって、Swiftで実装されたCocoa/CocoaTouchアプリケーションのクラスを推奨します。

  • NSObjectから拡張します。 Xcodeの新しいクラスダイアログは、この方向に進みます。
  • 動的ディスパッチのオーバーヘッドがパフォーマンスの問題につながる場合、静的ディスパッチを使用できます-たとえば、非常に小さなボディを持つメソッドの呼び出しを伴うタイトなループで。

概要:

  • SwiftはC++のように動作し、静的/ vtableの高速ディスパッチとリフレクションの制限があります。これにより、低レベルまたはパフォーマンス集約型のアプリケーションに適していますが、複雑さ、学習曲線、またはC++に関連するエラーのリスクはありません。
  • Swiftはコンパイルされた言語ですが、メソッド呼び出しのメッセージングスタイルは、Objective-Cとは異なり、Objective-CのないRubyやPythonなどの現代言語に見られる内省とダイナミズムを追加しますレガシー構文。

参照データ:メソッド呼び出しの実行オーバーヘッド:

  • 静的:<1.1ns
  • vtable:〜1.1ns
  • ダイナミック:〜4.9ns

(実際のパフォーマンスはハードウェアに依存しますが、比率は同じままです)。

また、動的属性により、メソッドが動的ディスパッチを使用する必要があることを明示的にSwiftに指示できるため、インターセプトがサポートされます。

public dynamic func foobar() -> AnyObject {
}
44
Jasper Blues

ドキュメントは、主に約動的型システムについて語っています

TypeおよびdynamicType

メタデータタイプ(言語リファレンス) を参照してください

例:

var clazz = TestObject.self
var instance: TestObject = clazz()

var type = instance.dynamicType

println("Type: \(type)") //Unfortunately this prints only "Type: Metatype"

TestObjectNSObjectを拡張すると仮定します

var clazz: NSObject.Type = TestObject.self
var instance : NSObject = clazz()

if let testObject = instance as? TestObject {
    println("yes!") //prints "yes!"
}

現在、実装されているリフレクションはありません。

編集: 私は明らかに間違っていた、ステベックスの答えを参照してください。おそらくIDEがオブジェクトの内容を検査できるようにするために、プロパティビルドにいくつかの単純な読み取り専用リフレクションがあります。

8
Sulthan

SwiftリフレクションAPIは、現時点ではAppleの優先度が高くないようです。しかし、@ stevex answer に加えて、標準ライブラリに役立つ別の関数があります。

ベータ6以降、_stdlib_getTypeNameは変数のマングル型名を取得します。これを空の遊び場に貼り付けます。

import Foundation

class PureSwiftClass {
}

var myvar0 = NSString() // Objective-C class
var myvar1 = PureSwiftClass()
var myvar2 = 42
var myvar3 = "Hans"

println( "TypeName0 = \(_stdlib_getTypeName(myvar0))")
println( "TypeName1 = \(_stdlib_getTypeName(myvar1))")
println( "TypeName2 = \(_stdlib_getTypeName(myvar2))")
println( "TypeName3 = \(_stdlib_getTypeName(myvar3))")

出力は次のとおりです。

TypeName0 = NSString
TypeName1 = _TtC13__lldb_expr_014PureSwiftClass
TypeName2 = _TtSi
TypeName3 = _TtSS

Ewan Swickのブログエントリ は、これらの文字列の解読に役立ちます。

例えば_TtSiは、Swiftの内部Intタイプを表します。

Mike Ashには同じトピックを扱った素晴らしいブログエントリがあります

6
Klaas

代わりにtoString()を使用することを検討してください。これはパブリックであり、_ stdlib_getTypeName()と同じように機能しますが、AnyClassでも機能する点が異なります。遊び場で

class MyClass {}

toString(MyClass.self) // evaluates to "__lldb_expr_49.MyClass"
5
silkentrance