web-dev-qa-db-fra.com

Est-ce que Swift supporte la réflexion?

Est-ce que Swift supporte la réflexion? Par exemple, y a-t-il quelque chose comme valueForKeyPath: et setValue:forKeyPath: pour Swift?

A-t-il même un système de type dynamique, quelque chose comme obj.class en Objective-C?

112
Khanh Nguyen

On dirait qu'il y a un début de support de réflexion:

class Fruit {
    var name="Apple"
}

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

De mchambers Gist, ici: https://Gist.github.com/mchambers/fb9da554898dae3e54f2

85
stevex

Si une classe étend NSObject, alors toute l'introspection et le dynamisme d'Objective-C fonctionnent. Ceci comprend:

  • Possibilité de demander à une classe ses méthodes et ses propriétés et d'appeler des méthodes ou de définir des propriétés.
  • La possibilité d'échanger des implémentations de méthodes. (ajouter des fonctionnalités à toutes les instances).
  • La possibilité de générer et d’affecter une nouvelle sous-classe à la volée. (ajouter des fonctionnalités à une instance donnée)

L'un des inconvénients de cette fonctionnalité est la prise en charge des types de valeur facultatifs Swift. Par exemple, les propriétés Int peuvent être énumérées et modifiées, mais pas les propriétés Int?. Les types facultatifs peuvent être énumérés partiellement à l'aide de reflect/MirrorType, mais tout de même non modifié.

Si une classe ne s'étend pas NSObject, alors seule la nouvelle réflexion très limitée (et en cours?) Fonctionne (voir reflect/MirrorType), ce qui ajoute une capacité limitée à interroger une instance sur sa classe et ses propriétés, mais aucune des fonctionnalités supplémentaires ci-dessus.

Lorsque vous n’étendez pas NSObject ou n’utilisez pas la directive '@objc', Swift est réglé par défaut sur la répartition statique et basée sur vtable. Ceci est plus rapide, mais en l’absence de machine virtuelle ne permet pas Interception de méthode d'exécution. Cette interception est un élément fondamental de Cocoa et est requise pour les types de fonctionnalités suivants:

  • Les observateurs de la propriété élégante de cacao. (Les observateurs de propriété sont directement intégrés à la Swift)).
  • Application non invasive de problèmes transversaux tels que la journalisation, la gestion des transactions (par exemple, la programmation orientée aspect).
  • Proxies, transfert de messages, etc.

Par conséquent, il est recommandé que les applications Cocoa/CocoaTouch implémentées avec Swift:

  • Étendre à partir de NSObject. Le nouveau dialogue de classe dans Xcode oriente dans cette direction.
  • Lorsque les frais généraux d'une répartition dynamique entraînent des problèmes de performances, vous pouvez utiliser la répartition statique, par exemple dans des boucles serrées avec des appels à des méthodes comportant de très petits corps.

Résumé:

  • Swift peut se comporter comme le C++, avec une distribution statique/vtable rapide et une réflexion limitée. Cela le rend adapté aux applications de niveau inférieur ou à hautes performances, mais sans la complexité, la courbe d'apprentissage ou le risque d'erreur associé au C++.
  • Alors que Swift est un langage compilé, le style de messagerie utilisé pour l’invocation de méthode ajoute l’introspection et le dynamisme des langages modernes tels que Ruby et Python, tout comme Objective-C , mais sans la syntaxe héritée d'Objective-C.

Données de référence: temps système d'exécution pour les appels de méthode:

  • statique: <1.1ns
  • vtable: ~ 1.1ns
  • dynamique: ~ 4.9ns

(Les performances réelles dépendent du matériel, mais les ratios resteront similaires).

En outre, l’attribut dynamique nous permet d’indiquer explicitement à Swift) qu’une méthode doit utiliser la répartition dynamique et prend donc en charge l’interception.

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

La documentation parle d’un système de types dynamique, principalement de

Type et dynamicType

Voir Type de métatype (dans la référence du langage)

Exemple:

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

var type = instance.dynamicType

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

Supposons maintenant que TestObject s'étend NSObject

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

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

Actuellement, aucune réflexion n'est mise en place.

MODIFIER: J'avais apparemment tort, voir la réponse de stevex. Il existe une simple réflexion en lecture seule pour les propriétés intégrées, probablement pour permettre aux IDE d’inspecter le contenu des objets.

8
Sulthan

Il semble qu'une API Swift réflexion ne soit pas une priorité élevée pour Apple pour le moment. Mais à part @stevex réponse il y a une autre fonction de la bibliothèque standard qui aide.

À partir de la version bêta 6 _stdlib_getTypeName obtient le nom de type mutilé d'une variable. Collez ceci dans un terrain de jeu vide:

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

La sortie est:

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

entrée de blog d'Ewan Swick aide à déchiffrer ces chaînes:

par exemple. _TtSi représente le type interne Int de Swift.

Mike Ash a une excellente entrée de blog couvrant le même sujet .

6
Klaas

Vous voudrez peut-être envisager d'utiliser toString () à la place. Il est public et fonctionne exactement comme _ stdlib_getTypeName () à la différence qu'il fonctionne également sur AnyClass, par exemple. dans un terrain de jeu entrer

class MyClass {}

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