116 votes

Est-ce que Swift prend en charge la réflexion ?

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

En fait, est-ce qu'il a même un système de types dynamique, quelque chose comme obj.class en Objective-C?

1 votes

J'ai créé une classe d'aide pour la réflexion en Swift. Vous pouvez la trouver à : github.com/evermeer/EVReflection

2 votes

Ils ont supprimé reflect dans Swift 2.0. Voici comment je numérote les attributs et les valeurs Link

86voto

stevex Points 2585

Il semble qu'il y ait le début d'une prise en charge des réflexions :

class Fruit {
    var name="Pomme"
}

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

De la part de mchambers gist, ici: https://gist.github.com/mchambers/fb9da554898dae3e54f2

0 votes

Merci. Infos très utiles!

5 votes

Eh bien, je ne considérerais pas cela comme une vraie réflexion. Pour une chose, c'est en lecture seule. Il me semble que c'est juste un hack pour activer le débogage dans Xcode. Le protocole Mirror cite en fait le mot IDE plusieurs fois.

7 votes

Et cela fonctionne uniquement pour les propriétés. Pas de réflexion de méthode.

46voto

Jasper Blues Points 7262

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

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

Un inconvénient de cette fonctionnalité est le support des types de valeurs optionnels de 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 optionnels peuvent être énumérés partiellement en utilisant reflect/MirrorType, mais toujours pas modifiés.

Si une classe n'étend pas NSObject, alors seul le nouveau, très limité (et en cours ?) fonctionnement de réflexion fonctionne (voir reflect/MirrorType), ce qui ajoute une capacité limitée à demander à une instance sa classe et ses propriétés, mais aucune des fonctionnalités supplémentaires ci-dessus.

Lorsqu'elle n'étend pas NSObject, ou utilise la directive '@objc', Swift utilise par défaut une expédition basée sur le statique et sur la table virtuelle. C'est plus rapide, cependant, en l'absence d'une machine virtuelle, elle n'autorise pas l'interception des méthodes à l'exécution. Cette interception est une partie fondamentale de Cocoa et est requise pour les types de fonctionnalités suivants :

  • Les observateurs élégants de propriétés de Cocoa (les observateurs de propriétés sont intégrés directement dans le langage Swift).
  • L'application non invasive de préoccupations transversales comme le logging, la gestion des transactions (c'est-à-dire la programmation orientée aspect).
  • Les proxys, la délégation de messages, etc.

Il est donc recommandé que les classes dans les applications Cocoa/CocoaTouch soient implémentées avec Swift :

  • Étendre à partir de NSObject. Le nouveau dialogue de classe dans Xcode oriente dans cette direction.
  • Lorsque le surcoût d'une expédition dynamique entraîne des problèmes de performances, l'expédition statique peut être utilisée - dans des boucles serrées avec des appels à des méthodes avec des corps très petits, par exemple.

Résumé :

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

Données de référence : Surcoût d'exécution pour les invocations de méthodes :

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

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

De plus, l'attribut dynamique nous permet d'instruire explicitement Swift qu'une méthode doit utiliser une expédition dynamique, et supportera donc l'interception.

public dynamic func foobar() -> AnyObject {
}

2 votes

Même en utilisant les techniques Objective-C, il semble que cela ne fonctionne pas pour les types optionnels Swift. Je suggérerais de noter cette limitation dans la réponse à moins que je ne manque une astuce.

8voto

Sulthan Points 23360

La documentation parle d'un système de type dynamique, principalement à propos de

Type et dynamicType

Voir Type Métatypé (dans la Référence du Langage)

Exemple :

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

var type = instance.dynamicType

println("Type: \(type)") //Malheureusement cela affiche seulement "Type: Métatypé"

Maintenant en supposant que TestObject étend NSObject

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

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

Actuellement, il n'y a pas de réflexion implémentée.

EDIT: Apparemment, j'avais tort, voir la réponse de stevex. Il y a une réflexion simple en lecture seule pour les propriétés intégrée, probablement pour permettre aux IDE d'inspecter le contenu de l'objet.

6voto

Jacky Points 442

Pas de mot-clé reflect en Swift 5, maintenant vous pouvez utiliser

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

var moi = Person()
var miroir = Mirror(reflecting: moi)

for case let (label?, value) in miroir.children {
    print (label, value)
}

0 votes

Pourquoi cela n'est-il pas voté ? C'est très utile. Je vais l'appliquer pour la désérialisation JSON.

5voto

silkentrance Points 51

Vous voudrez peut-être envisager d'utiliser toString() à la place. Il est public et fonctionne de la même manière que _stdlib_getTypeName() à la différence qu'il fonctionne également sur AnyClass, par exemple dans un Playground saisissez

class MyClass {}

toString(MyClass.self) // évalue à "__lldb_expr_49.MyClass"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X