web-dev-qa-db-fra.com

Comment imprimer le type ou la classe d'une variable dans Swift?

Existe-t-il un moyen d’imprimer le type d’exécution d’une variable dans swift? Par exemple:

var now = NSDate()
var soon = now.dateByAddingTimeInterval(5.0)

println("\(now.dynamicType)") 
// Prints "(Metatype)"

println("\(now.dynamicType.description()")
// Prints "__NSDate" since objective-c Class objects have a "description" selector

println("\(soon.dynamicType.description()")
// Compile-time error since ImplicitlyUnwrappedOptional<NSDate> has no "description" method

Dans l'exemple ci-dessus, je cherche un moyen de montrer que la variable "bientôt" est de type ImplicitlyUnwrappedOptional<NSDate> ou au moins NSDate!.

313
Matt Bridges

Mise à jour septembre 2016

Swift 3.0: utilisez type(of:), par exemple type(of: someThing) (puisque le mot clé dynamicType a été supprimé)

Mise à jour octobre 2015:

J'ai mis à jour les exemples ci-dessous avec la nouvelle syntaxe Swift 2.0 (par exemple, println a été remplacé par print, toString() est maintenant String()).

À partir des notes de publication de Xcode 6.3:

@nschum signale dans les commentaires que les notes de publication de Xcode 6.3 montrent une autre manière:

Les valeurs de type sont maintenant imprimées sous le nom de type totalement démêlé lorsqu'elles sont utilisées avec println ou interpolation de chaîne.

import Foundation

class PureSwiftClass { }

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

print( "String(myvar0.dynamicType) -> \(myvar0.dynamicType)")
print( "String(myvar1.dynamicType) -> \(myvar1.dynamicType)")
print( "String(myvar2.dynamicType) -> \(myvar2.dynamicType)")
print( "String(myvar3.dynamicType) -> \(myvar3.dynamicType)")

print( "String(Int.self)           -> \(Int.self)")
print( "String((Int?).self         -> \((Int?).self)")
print( "String(NSString.self)      -> \(NSString.self)")
print( "String(Array<String>.self) -> \(Array<String>.self)")

Quelles sorties:

String(myvar0.dynamicType) -> __NSCFConstantString
String(myvar1.dynamicType) -> PureSwiftClass
String(myvar2.dynamicType) -> Int
String(myvar3.dynamicType) -> String
String(Int.self)           -> Int
String((Int?).self         -> Optional<Int>
String(NSString.self)      -> NSString
String(Array<String>.self) -> Array<String>

Mise à jour pour Xcode 6.3:

Vous pouvez utiliser la _stdlib_getDemangledTypeName():

print( "TypeName0 = \(_stdlib_getDemangledTypeName(myvar0))")
print( "TypeName1 = \(_stdlib_getDemangledTypeName(myvar1))")
print( "TypeName2 = \(_stdlib_getDemangledTypeName(myvar2))")
print( "TypeName3 = \(_stdlib_getDemangledTypeName(myvar3))")

et obtenez ceci en sortie:

TypeName0 = NSString
TypeName1 = __lldb_expr_26.PureSwiftClass
TypeName2 = Swift.Int
TypeName3 = Swift.String

_ {Réponse originale:} _

Avant Xcode 6.3, _stdlib_getTypeName obtenait le nom de type mutilé d'une variable. L'entrée de blog d'Ewan Swick aide à déchiffrer ces chaînes: 

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

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

350
Klaas

Edit: Une nouvelle fonction toStringa été introduite dans Swift 1.2 (Xcode 6.3) .

Vous pouvez maintenant imprimer le type démêlé de tout type en utilisant .self et toute instance en utilisant .dynamicType:

struct Box<T> {}

toString("foo".dynamicType)            // Swift.String
toString([1, 23, 456].dynamicType)     // Swift.Array<Swift.Int>
toString((7 as NSNumber).dynamicType)  // __NSCFNumber

toString((Bool?).self)                 // Swift.Optional<Swift.Bool>
toString(Box<SinkOf<Character>>.self)  // __lldb_expr_1.Box<Swift.SinkOf<Swift.Character>>
toString(NSStream.self)                // NSStream

Essayez d'appeler YourClass.self et yourObject.dynamicType.

Référence: https://devforums.Apple.com/thread/227425 .

45
akashivskyy

Est-ce ce que vous cherchez?

println("\(object_getClassName(now))");

Il imprime "__NSDate"

UPDATE: Veuillez noter que cela ne semble plus fonctionner à partir de Beta05

30
Haiku Oezu

Swift 3.0

let string = "Hello"
let stringArray = ["one", "two"]
let dictionary = ["key": 2]

print(type(of: string)) // "String"

// Get type name as a string
String(describing: type(of: string)) // "String"
String(describing: type(of: stringArray)) // "Array<String>"
String(describing: type(of: dictionary)) // "Dictionary<String, Int>"

// Get full type as a string
String(reflecting: type(of: string)) // "Swift.String"
String(reflecting: type(of: stringArray)) // "Swift.Array<Swift.String>"
String(reflecting: type(of: dictionary)) // "Swift.Dictionary<Swift.String, Swift.Int>"
29
Evgenii

Mon Xcode actuel est la version 6.0 (6A280e).

import Foundation

class Person { var name: String; init(name: String) { self.name = name }}
class Patient: Person {}
class Doctor: Person {}

var variables:[Any] = [
    5,
    7.5,
    true,
    "maple",
    Person(name:"Sarah"),
    Patient(name:"Pat"),
    Doctor(name:"Sandy")
]

for variable in variables {
    let typeLongName = _stdlib_getDemangledTypeName(variable)
    let tokens = split(typeLongName, { $0 == "." })
    if let typeName = tokens.last {
        println("Variable \(variable) is of Type \(typeName).")
    }
}

Sortie:

Variable 5 is of Type Int.
Variable 7.5 is of Type Double.
Variable true is of Type Bool.
Variable maple is of Type String.
Variable Swift001.Person is of Type Person.
Variable Swift001.Patient is of Type Patient.
Variable Swift001.Doctor is of Type Doctor.
23
Matt Hagen

À partir de Xcode 6.3 avec Swift 1.2, vous pouvez simplement convertir les valeurs de type en String complètement démêlé.

toString(Int)                   // "Swift.Int"
toString(Int.Type)              // "Swift.Int.Type"
toString((10).dynamicType)      // "Swift.Int"
println(Bool.self)              // "Swift.Bool"
println([UTF8].self)            // "Swift.Array<Swift.UTF8>"
println((Int, String).self)     // "(Swift.Int, Swift.String)"
println((String?()).dynamicType)// "Swift.Optional<Swift.String>"
println(NSDate)                 // "NSDate"
println(NSDate.Type)            // "NSDate.Type"
println(WKWebView)              // "WKWebView"
toString(MyClass)               // "[Module Name].MyClass"
toString(MyClass().dynamicType) // "[Module Name].MyClass"
18
kiding

Vous pouvez toujours accéder à la classe via className (qui renvoie String).

Il existe en fait plusieurs façons d'obtenir la classe, par exemple classForArchiver, classForCoder, classForKeyedArchiver (tous renvoient AnyClass!).

Vous ne pouvez pas obtenir le type d'une primitive (une primitive est pas une classe).

Exemple:

var ivar = [:]
ivar.className // __NSDictionaryI

var i = 1
i.className // error: 'Int' does not have a member named 'className'

Si vous voulez obtenir le type d'une primitive, vous devez utiliser bridgeToObjectiveC(). Exemple:

var i = 1
i.bridgeToObjectiveC().className // __NSCFNumber
17
Leandros

Vous pouvez utiliser reflect pour obtenir des informations sur l'objet.
Par exemple, nom de la classe d'objet:

 var classname = reflect (maintenant) .summary 
16
Stan

J'ai eu de la chance avec:

let className = NSStringFromClass(obj.dynamicType)
13
mxcl

Xcode 8 Swift 3.0 type d'utilisation (de :)

let className = "\(type(of: instance))"
12
Hari Kunwar

Dans Xcode 8, Swift 3.0

Pas:

1. Obtenez le type:

Option 1:

let type : Type = MyClass.self  //Determines Type from Class

Option 2:

let type : Type = type(of:self) //Determines Type from self

2. Convertir le type en chaîne:

let string : String = "\(type)" //String
10
user1046037

Dans Swift 3.0, vous pouvez utiliser type(of:), le mot clé dynamicType ayant été supprimé.

8
BrunoSerrano

Swift 3

Avec la dernière version de Swift 3, nous pouvons obtenir de jolies descriptions de noms de types via l'initialiseur String. Comme, par exemple, print(String(describing: type(of: object))). Où objectpeut être une variable d'instance comme un tableau, un dictionnaire, un Int, un NSDate, une instance d'une classe personnalisée, etc.

Voici ma réponse complète: Obtenir le nom de classe de l'objet sous forme de chaîne dans Swift

Cette question cherche un moyen d'obtenir le nom de classe d'un objet sous forme de chaîne, mais j'ai également proposé un autre moyen d'obtenir le nom de classe d'une variable qui n'est pas la sous-classe de NSObject. C'est ici: 

class Utility{
    class func classNameAsString(obj: Any) -> String {
        //prints more readable results for dictionaries, arrays, Int, etc
        return String(describing: type(of: obj))
    }
}

J'ai créé une fonction statique qui prend en paramètre un objet de type Any et renvoie son nom de classe sous la forme String :).

J'ai testé cette fonction avec des variables telles que: 

    let diccionary: [String: CGFloat] = [:]
    let array: [Int] = []
    let numInt = 9
    let numFloat: CGFloat = 3.0
    let numDouble: Double = 1.0
    let classOne = ClassOne()
    let classTwo: ClassTwo? = ClassTwo()
    let now = NSDate()
    let lbl = UILabel()

et la sortie était:

  • dictionnaire est de type Dictionnaire
  • array est de type Array
  • numInt est de type Int
  • numFloat est de type CGFloat
  • numDouble est de type Double
  • classOne est de type: ClassOne
  • classTwo est de type: ClassTwo
  • est maintenant de type: Date
  • lbl est de type: UILabel
6
mauricioconde

La réponse principale ne contient pas d'exemple concret de la nouvelle manière de procéder utilisant type(of:. Donc, pour aider les recrues comme moi, voici un exemple pratique, tiré principalement de la documentation d’Apple - https://developer.Apple.com/documentation/Swift/2885064-type

doubleNum = 30.1

func printInfo(_ value: Any) {
    let varType = type(of: value)
    print("'\(value)' of type '\(varType)'")
}

printInfo(doubleNum)
//'30.1' of type 'Double'
5
Joshua Dance

J'ai essayé quelques-unes des autres réponses ici, mais Milage semble trop définir l'objet sous-jacent.

Cependant, j'ai trouvé un moyen d'obtenir le nom de la classe Object-C pour un objet en procédant comme suit:

now?.superclass as AnyObject! //replace now with the object you are trying to get the class name for

Voici un exemple d'utilisation:

let now = NSDate()
println("what is this = \(now?.superclass as AnyObject!)")

Dans ce cas, NSDate sera imprimé dans la console.

4
James Jones

J'ai trouvé cette solution qui, espérons-le, pourrait fonctionner pour quelqu'un d'autre ... J'ai créé une méthode de classe pour accéder à la valeur. N'oubliez pas que cela fonctionnera uniquement pour la sous-classe NSObject. Mais au moins, c'est une solution propre et ordonnée.

class var className: String!{
    let classString : String = NSStringFromClass(self.classForCoder())
    return classString.componentsSeparatedByString(".").last;
}
4
DennyLou

Lorsque vous utilisez Cocoa (et non CocoaTouch), vous pouvez utiliser la propriété className pour les objets qui sont des sous-classes de NSObject. 

println(now.className)

Cette propriété n'est pas disponible pour les objets Swift normaux, qui ne sont pas des sous-classes de NSObject (et en fait, il n'y a pas d'identifiant racine ni de type d'objet dans Swift).

class Person {
    var name: String?
}

var p = Person()
println(person.className) // <- Compiler error

Dans CocoaTouch, il n’existe à ce jour aucun moyen d’obtenir une description sous forme de chaîne du type d’une variable donnée. Des fonctionnalités similaires n'existent pas non plus pour les types primitifs dans Cocoa ou CocoaTouch.

Le Swift REPL est capable d’imprimer un résumé des valeurs, y compris son type. Il est donc possible que ce type d’introspection soit possible à l’avenir via un API.

EDIT:dump(object) semble faire l'affaire.

4
Matt Bridges

Dans la dernière version de XCode 6.3 avec Swift 1.2, voici le seul moyen que j'ai trouvé:

if view.classForCoder.description() == "UISegment" {
    ...
}
4
Joel Kopelioff

Sur la base des réponses et commentaires donnés par Klass et Kevin Ballard ci-dessus, j'irais avec:

println(_stdlib_getDemangledTypeName(now).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon?).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon!).componentsSeparatedByString(".").last!)

println(_stdlib_getDemangledTypeName(myvar0).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar1).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar2).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar3).componentsSeparatedByString(".").last!)

qui imprimera:

"NSDate"
"ImplicitlyUnwrappedOptional"
"Optional"
"NSDate"

"NSString"
"PureSwiftClass"
"Int"
"Double"
3
Vince O'Sullivan
let i: Int = 20


  func getTypeName(v: Any) -> String {
    let fullName = _stdlib_demangleName(_stdlib_getTypeName(i))
    if let range = fullName.rangeOfString(".") {
        return fullName.substringFromIndex(range.endIndex)
    }
    return fullName
}

println("Var type is \(getTypeName(i)) = \(i)")
3
Alex Burla

Dans lldb à partir de la version bêta 5, vous pouvez voir la classe d'un objet avec la commande:

fr v -d r shipDate

qui produit quelque chose comme:

(DBSalesOrderShipDate_DBSalesOrderShipDate_ *) shipDate = 0x7f859940

La commande développée signifie quelque chose comme:

Frame Variable (imprimer une variable d'image) -d run_target (développer les types dynamiques)

Il est utile de savoir que l'utilisation de "Variable de trame" pour afficher les valeurs de variable garantit qu'aucun code n'est exécuté.

La plupart des réponses ne fonctionnent pas avec la dernière version de Swift (Xcode 7.1.1 au moment de la rédaction).

Le moyen actuel d'obtenir les informations consiste à créer une variable Mirror et à l'interroger. Pour le nom de la classe, c'est aussi simple que:

let mirror = Mirror(reflecting: instanceToInspect)
let classname:String = mirror.description

Des informations supplémentaires sur l'objet peuvent également être extraites de la variable Mirror. Voir http://swiftdoc.org/v2.1/type/Mirror/ pour plus de détails.

3
Joseph Lord

Swift version 4:

print("\(type(of: self)) ,\(#function)")
// within a function of a class

Merci @Joshua Dance

2
dengApro

Il semble n'y avoir aucun moyen générique pour imprimer le nom de type d'un type de valeur arbitraire. Comme d'autres l'ont noté, pour class instances, vous pouvez imprimer value.className, mais pour les valeurs primitives, il semble qu'au moment de l'exécution, les informations de type ont disparu. 

Par exemple, il semble qu'il n'y ait pas de moyen de taper: 1.something() et d'extraire Int pour toute valeur de something. (Vous pouvez, comme l’a suggéré une autre réponse, utiliser i.bridgeToObjectiveC().className pour vous donner un indice, mais __NSCFNumber est non en fait le type de i - exactement ce à quoi il sera converti lorsqu'il franchira la limite d’une fonction Objective-C appel.)

Je serais heureux de me tromper, mais il semblerait que la vérification du type soit entièrement effectuée au moment de la compilation et que, comme le C++ (avec RTTI désactivé), une grande partie des informations sur le type a disparu au moment de l'exécution.

2
ipmcc

Voici comment vous obtenez une chaîne de caractères de votre objet ou Type qui est consistent et qui prend en compte à quel module la définition d'objet appartient ou est imbriquée. Works in Swift 4.x.

@inline(__always) func typeString(for _type: Any.Type) -> String {
    return String(reflecting: type(of: _type))
}

@inline(__always) func typeString(for object: Any) -> String {
    return String(reflecting: type(of: type(of: object)))
}

struct Lol {
    struct Kek {}
}

// if you run this in playground the results will be something like
typeString(for: Lol.self)    //    __lldb_expr_74.Lol.Type
typeString(for: Lol())       //    __lldb_expr_74.Lol.Type
typeString(for: Lol.Kek.self)//    __lldb_expr_74.Lol.Kek.Type
typeString(for: Lol.Kek())   //    __lldb_expr_74.Lol.Kek.Type
2
Igor Muzyka

Swift 4:

// "TypeName"
func stringType(of some: Any) -> String {
    let string = (some is Any.Type) ? String(describing: some) : String(describing: type(of: some))
    return string
}

// "ModuleName.TypeName"
func fullStringType(of some: Any) -> String {
    let string = (some is Any.Type) ? String(reflecting: some) : String(reflecting: type(of: some))
    return string
}

Usage:

print(stringType(of: SomeClass()))     // "SomeClass"
print(stringType(of: SomeClass.self))  // "SomeClass"
print(stringType(of: String()))        // "String"
print(fullStringType(of: String()))    // "Swift.String"
2
Desmond Hume

Ce n'est pas exactement ce que vous recherchez, mais vous pouvez également vérifier le type de la variable par rapport aux types Swift comme ceci:

let object: AnyObject = 1

if object is Int {
}
else if object is String {
}

Par exemple.

1

Swift 3.0, Xcode 8

Avec le code suivant, vous pouvez demander à une instance sa classe. Vous pouvez également comparer deux instances, si elles ont la même classe.

// CREATE pure Swift class
class MySwiftClass {
    var someString : String = "default"
    var someInt    : Int = 5
}

// CREATE instances
let firstInstance = MySwiftClass()
let secondInstance = MySwiftClass()
secondInstance.someString = "Donald"
secondInstance.someInt = 24

// INSPECT instances
if type(of: firstInstance) === MySwiftClass.self {
    print("SUCCESS with ===")
} else {
    print("PROBLEM with ===")
}

if type(of: firstInstance) == MySwiftClass.self {
    print("SUCCESS with ==")
} else {
    print("PROBLEM with ==")
}

// COMPARE CLASS OF TWO INSTANCES
if type(of: firstInstance) === type(of: secondInstance) {
    print("instances have equal class")
} else {
    print("instances have NOT equal class")
}
1
LukeSideWalker

Pour obtenir un type d'objet ou classe d'objet dans Swift, vous devez utiliser un type (de: votreObjet) 

type (de: votre objet)

1
Rahul Panzade

Veuillez regarder l'extrait de code ci-dessous et laissez-moi savoir si vous cherchez quelque chose comme ci-dessous ou non.

var now = NSDate()
var soon = now.addingTimeInterval(5.0)

var nowDataType = Mirror(reflecting: now)
print("Now is of type: \(nowDataType.subjectType)")

var soonDataType = Mirror(reflecting: soon)
print("Soon is of type: \(soonDataType.subjectType)")
0
DILIP KOSURI

Ceci est également utile pour vérifier si un objet est un type de classe:

if someObject is SomeClass {
    //someObject is a type of SomeClass
}
0
Nam

Un autre aspect important qui influence le nom de classe renvoyé par String(describing: type(of: self)) est Contrôle d'accès .

Prenons l'exemple suivant, basé sur Swift 3.1.1 , Xcode 8.3.3 (juillet 2017)

func printClassNames() {

    let className1 = SystemCall<String>().getClassName()
    print(className1) // prints: "SystemCall<String>"

    let className2 = DemoSystemCall().getClassName()
    print(className2) // prints: "DemoSystemCall"

    // private class example
    let className3 = PrivateDemoSystemCall().getClassName()
    print(className3) // prints: "(PrivateDemoSystemCall in _0FC31E1D2F85930208C245DE32035247)" 

    // fileprivate class example
    let className4 = FileprivateDemoSystemCall().getClassName()
    print(className4) // prints: "(FileprivateDemoSystemCall in _0FC31E1D2F85930208C245DE32035247)" 
}

class SystemCall<T> {
    func getClassName() -> String {
        return String(describing: type(of: self))
    }
}

class DemoSystemCall: SystemCall<String> { }

private class PrivateDemoSystemCall: SystemCall<String> { }

fileprivate class FileprivateDemoSystemCall: SystemCall<String> { }

Comme vous pouvez le constater, toutes les classes de cet exemple ont différents niveaux de contrôle d'accès qui influencent leur représentation String. Si les classes ont des niveaux de contrôle d'accès private ou fileprivate, Swift semble ajouter une sorte d'identificateur lié à la classe "d'emboîtement" de la classe en question.

Le résultat pour PrivateDemoSystemCall et FileprivateDemoSystemCall est que le même identifiant est ajouté car ils sont tous deux imbriqués dans la même classe parente.

Je n'ai pas encore trouvé le moyen de m'en débarrasser, mis à part quelques fonctions de remplacement de hacky ou de fonction regex.

Juste mes 2 cents.

0
Philipp Jahoda