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!
.
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 .
Edit: Une nouvelle fonction toString
a é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 .
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
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>"
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.
À 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"
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
Vous pouvez utiliser reflect pour obtenir des informations sur l'objet.
Par exemple, nom de la classe d'objet:
var classname = reflect (maintenant) .summary
J'ai eu de la chance avec:
let className = NSStringFromClass(obj.dynamicType)
Xcode 8 Swift 3.0 type d'utilisation (de :)
let className = "\(type(of: instance))"
let type : Type = MyClass.self //Determines Type from Class
let type : Type = type(of:self) //Determines Type from self
let string : String = "\(type)" //String
Dans Swift 3.0, vous pouvez utiliser type(of:)
, le mot clé dynamicType
ayant été supprimé.
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ù object
peut ê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:
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'
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.
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;
}
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.
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" {
...
}
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"
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)")
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.
Swift version 4:
print("\(type(of: self)) ,\(#function)")
// within a function of a class
Merci @Joshua Dance
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.
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
// "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
}
print(stringType(of: SomeClass())) // "SomeClass"
print(stringType(of: SomeClass.self)) // "SomeClass"
print(stringType(of: String())) // "String"
print(fullStringType(of: String())) // "Swift.String"
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.
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")
}
Pour obtenir un type d'objet ou classe d'objet dans Swift, vous devez utiliser un type (de: votreObjet)
type (de: votre objet)
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)")
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
}
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.