Unterstützt Swift Reflexion?

113

Unterstützt Swift Reflexion? zB gibt es so etwas wie valueForKeyPath:und setValue:forKeyPath:für Swift-Objekte?

Hat es überhaupt ein dynamisches Typsystem, so etwas wie obj.classin Objective-C?

Khanh Nguyen
quelle
1
Ich habe eine Hilfsklasse zum Nachdenken in Swift erstellt. Sie finden es unter: github.com/evermeer/EVReflection
Edwin Vermeer
2
Sie haben Reflect in Swift 2.0 entfernt. So
zähle

Antworten:

85

Es sieht so aus, als würde eine Reflexionsunterstützung beginnen:

class Fruit {
    var name="Apple"
}

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

Von mchambers gist hier: https://gist.github.com/mchambers/fb9da554898dae3e54f2

stevex
quelle
5
Nun, ich würde das nicht als echte Reflexion betrachten. Zum einen ist es nur lesbar. Mir scheint, es ist nur ein Hack, um das Debuggen in Xcode zu ermöglichen. Das Protokoll Mirrorzitiert das Wort tatsächlich IDEmehrmals.
Sulthan
7
Und es funktioniert nur für Immobilien. Keine Methodenreflexion.
Sulthan
11
Ich habe dies im Swift-Labor des WWDC geschrieben und dachte, ich würde den Rest teilen. Wie alle herausgefunden haben, haben die Ingenieure, mit denen ich gesprochen habe, bestätigt, dass die Funktion reflex () zur Unterstützung des Spielplatzes vorhanden ist. Aber du kannst trotzdem ein bisschen Spaß damit haben :) hier habe ich einen kleinen Modell-Serializer damit gehackt. Fügen Sie es in den Spielplatz ein und haben Sie Spaß: gist.github.com/mchambers/67640d9c3e2bcffbb1e2
Marc Chambers
1
Schauen Sie sich die Antwort stackoverflow.com/a/25345461/292145 an, um herauszufinden, wie Sie _stdlib_getTypeNamehelfen können.
Klaas
1
Hier ist eine Klasse, die Basisklassen und Optionen (keine Typen) widerspiegelt und NSCoding und das Parsen von und zu einem Wörterbuch unterstützt: github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/…
Edwin Vermeer
44

Wenn eine Klasse erweitert wird NSObject, funktioniert die gesamte Selbstbeobachtung und Dynamik von Objective-C. Das beinhaltet:

  • Die Möglichkeit, eine Klasse nach ihren Methoden und Eigenschaften zu fragen und Methoden aufzurufen oder Eigenschaften festzulegen.
  • Die Fähigkeit, Methodenimplementierungen auszutauschen. (Fügen Sie allen Instanzen Funktionen hinzu).
  • Die Fähigkeit, im laufenden Betrieb eine neue Unterklasse zu generieren und zuzuweisen. (Hinzufügen von Funktionen zu einer bestimmten Instanz)

Ein Nachteil dieser Funktionalität ist die Unterstützung optionaler Swift-Werttypen. Zum Beispiel können Int-Eigenschaften aufgezählt und geändert werden, aber Int? Eigenschaften können nicht. Optionale Typen können teilweise mit Reflect / MirrorType aufgelistet, aber immer noch nicht geändert werden.

Wenn eine Klasse nicht erweitert wird NSObject, funktioniert nur die neue, sehr eingeschränkte (und in Bearbeitung befindliche?) Reflexion (siehe Reflect / MirrorType), wodurch eine Instanz nur eingeschränkt nach ihrer Klasse und ihren Eigenschaften gefragt werden kann, jedoch keine der oben genannten zusätzlichen Funktionen .

Wenn Sie NSObject nicht erweitern oder die Direktive '@objc' verwenden, verwendet Swift standardmäßig den statischen und vtable-basierten Versand. Dies ist jedoch schneller, da in Abwesenheit einer virtuellen Maschine kein Abfangen von Laufzeitmethoden möglich ist. Dieses Abfangen ist ein wesentlicher Bestandteil von Cocoa und wird für die folgenden Arten von Funktionen benötigt:

  • Kakaos elegante Immobilienbeobachter. (Immobilienbeobachter sind direkt in die Swift-Sprache eingebrannt).
  • Nicht-invasive Anwendung von Querschnittsthemen wie Protokollierung, Transaktionsmanagement (dh aspektorientierte Programmierung).
  • Proxies, Nachrichtenweiterleitung usw.

Daher wird empfohlen, Klassen in Cocoa / CocoaTouch-Anwendungen mit Swift zu implementieren:

  • Aus NSObject erweitern. Der neue Klassendialog in Xcode steuert in diese Richtung.
  • Wenn der Overhead eines dynamischen Versands zu Leistungsproblemen führt, kann der statische Versand verwendet werden - beispielsweise in engen Schleifen mit Aufrufen von Methoden mit sehr kleinen Körpern.

Zusammenfassung:

  • Swift kann sich wie C ++ verhalten, mit schnellem statischen / vtable-Versand und begrenzter Reflexion. Dies macht es für Anwendungen auf niedrigerer Ebene oder für leistungsintensive Anwendungen geeignet, jedoch ohne die Komplexität, Lernkurve oder das mit C ++ verbundene Fehlerrisiko
  • Während Swift eine kompilierte Sprache ist, fügt der Messaging-Stil des Methodenaufrufs die Selbstbeobachtung und Dynamik hinzu, die in modernen Sprachen wie Ruby und Python zu finden sind, genau wie in Objective-C, jedoch ohne die Legacy-Syntax von Objective-C.

Referenzdaten: Ausführungsaufwand für Methodenaufrufe:

  • statisch: <1.1ns
  • vtable: ~ 1.1ns
  • dynamisch: ~ 4,9 ns

(Die tatsächliche Leistung hängt von der Hardware ab, die Verhältnisse bleiben jedoch ähnlich.)

Das dynamische Attribut ermöglicht es uns außerdem, Swift explizit anzuweisen, dass eine Methode den dynamischen Versand verwenden soll, und unterstützt daher das Abfangen.

public dynamic func foobar() -> AnyObject {
}
Jasper Blues
quelle
2
Selbst mit Objective-C-Techniken scheint es für optionale Swift-Typen nicht zu funktionieren. Ich würde vorschlagen, diese Einschränkung in der Antwort zu vermerken, es sei denn, ich vermisse einen Trick.
Whitneyland
8

Die Dokumentation spricht hauptsächlich von einem dynamischen Typsystem

Type und dynamicType

Siehe Metatyp Typ (in der Sprachreferenz)

Beispiel:

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

var type = instance.dynamicType

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

Nun geht man davon aus, dass es sich TestObjectausdehntNSObject

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

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

Derzeit ist keine Reflexion implementiert.

EDIT: Ich habe mich anscheinend geirrt, siehe stevex 'Antwort. Es gibt einige einfache schreibgeschützte Überlegungen zu eingebauten Eigenschaften, wahrscheinlich um IDEs die Möglichkeit zu geben, Objektinhalte zu überprüfen.

Sulthan
quelle
6

Es scheint, dass eine Swift Reflection API für Apple derzeit keine hohe Priorität hat. Neben der Antwort von @stevex gibt es in der Standardbibliothek noch eine weitere Funktion, die hilft.

Ab Beta 6 _stdlib_getTypeNamewird der verstümmelte Typname einer Variablen abgerufen. Fügen Sie dies in einen leeren Spielplatz ein:

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))")

Die Ausgabe ist:

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

Der Blogeintrag von Ewan Swick hilft dabei, diese Zeichenfolgen zu entschlüsseln:

zB _TtSisteht für Swifts internen IntTyp.

Mike Ash hat einen großartigen Blogeintrag zum gleichen Thema .

Klaas
quelle
@aleclarson Ja, das ist auch sehr nützlich.
Klaas
1
sind das nicht private API? Wird Apple die App genehmigen, wenn sie verwendet wird?
Eduardo Costa
@ EduardoCosta ja sicher. Sie sind privat. Ich benutze sie nur für Debug-Builds.
Klaas
Hier ist ein aktualisierter Link zu Ewan Swicks Blog-Artikel: eswick.com/2014/06/08/Inside-Swift
RenniePet
5

Möglicherweise möchten Sie stattdessen toString () verwenden. Es ist öffentlich und funktioniert genauso wie _stdlib_getTypeName (), mit dem Unterschied, dass es auch auf AnyClass funktioniert , z. B. auf einem Spielplatz

class MyClass {}

toString(MyClass.self) // evaluates to "__lldb_expr_49.MyClass"
Seideneintritt
quelle
1

Kein reflectSchlüsselwort in Swift 5, jetzt können Sie verwenden

struct Person {
    var name="name"
    var age = 15
}

var me = Person()
var mirror = Mirror(reflecting: me)

for case let (label?, value) in mirror.children {
    print (label, value)
}
Jacky
quelle
Warum wird das nicht positiv bewertet? Dies ist sehr nützlich. Ich werde es für die jsonDeserialisierung anwenden
javadba