Gibt es eine Möglichkeit, den Laufzeittyp einer Variablen schnell zu drucken? Zum Beispiel:
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
Im obigen Beispiel suche ich nach einer Möglichkeit, zu zeigen, dass die Variable "soon" den Typ ImplicitlyUnwrappedOptional<NSDate>
oder zumindest NSDate!
hat.
Update September 2016
Swift 3.0: Verwenden Sie type(of:)
, z. type(of: someThing)
(seit das dynamicType
-Schlüsselwort entfernt wurde)
Update Oktober 2015:
Ich habe die folgenden Beispiele auf die neue Swift 2.0-Syntax aktualisiert (beispielsweise wurde println
durch print
ersetzt, toString()
ist jetzt String()
).
Aus den Versionshinweisen zu Xcode 6.3:
@nschum weist in den Kommentaren darauf hin, dass die Versionshinweise zu Xcode 6.3 einen anderen Weg zeigen:
Wenn Werte mit .__ verwendet werden, werden Werte jetzt als vollständiger entmusterter Typenname gedruckt. println oder string interpolation.
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)")
Welche Ausgänge:
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>
Update für Xcode 6.3:
Sie können die _stdlib_getDemangledTypeName()
verwenden:
print( "TypeName0 = \(_stdlib_getDemangledTypeName(myvar0))")
print( "TypeName1 = \(_stdlib_getDemangledTypeName(myvar1))")
print( "TypeName2 = \(_stdlib_getDemangledTypeName(myvar2))")
print( "TypeName3 = \(_stdlib_getDemangledTypeName(myvar3))")
und bekomme dies als Ausgabe:
TypeName0 = NSString
TypeName1 = __lldb_expr_26.PureSwiftClass
TypeName2 = Swift.Int
TypeName3 = Swift.String
Ursprüngliche Antwort:
Vor Xcode 6.3 erhielt _stdlib_getTypeName
den unzulässigen Typnamen einer Variablen. Ewan Swicks Blogeintrag hilft beim Entschlüsseln dieser Zeichenfolgen:
z.B. _TtSi
steht für den internen Int
-Typ von Swift.
Mike Ash hat einen großartigen Blogeintrag zum selben Thema .
Bearbeiten: Eine neue toString
-Funktion wurde in Swift 1.2 (Xcode 6.3) eingeführt.
Sie können jetzt den entmusterten Typ eines beliebigen Typs mit .self
und einer beliebigen Instanz mit .dynamicType
drucken:
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
Versuchen Sie, YourClass.self
und yourObject.dynamicType
aufzurufen.
Referenz: https://devforums.Apple.com/thread/227425 .
Ist es das, wonach du suchst?
println("\(object_getClassName(now))");
Es druckt "__NSDate"
UPDATE: Bitte beachten Sie, dass dies ab Beta05 nicht mehr funktioniert
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>"
Mein aktueller Xcode ist 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).")
}
}
Ausgabe:
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.
Ab Xcode 6.3 mit Swift 1.2 können Sie die Werte einfach in die vollständig gemischten Variablen String
konvertieren.
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"
Sie können weiterhin über className
(die eine String
zurückgibt) auf die Klasse zugreifen.
Es gibt mehrere Möglichkeiten, die Klasse abzurufen, beispielsweise classForArchiver
, classForCoder
, classForKeyedArchiver
(alle geben AnyClass!
) zurück.
Sie können den Typ eines Primitivs nicht ermitteln (ein Primitiv ist nicht eine Klasse).
Beispiel:
var ivar = [:]
ivar.className // __NSDictionaryI
var i = 1
i.className // error: 'Int' does not have a member named 'className'
Wenn Sie den Typ eines Primitivs erhalten möchten, müssen Sie bridgeToObjectiveC()
verwenden. Beispiel:
var i = 1
i.bridgeToObjectiveC().className // __NSCFNumber
Sie können reflektieren verwenden, um Informationen zum Objekt zu erhalten.
Zum Beispiel Name der Objektklasse:
var classname = reflect (jetzt) .summary
Ich hatte Glück mit:
let className = NSStringFromClass(obj.dynamicType)
Xcode 8 Swift 3.0 Verwendungstyp (von :)
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
In Swift 3.0 können Sie type(of:)
verwenden, da das dynamicType
-Schlüsselwort entfernt wurde.
Swift 3
Mit der neuesten Version von Swift 3 können Sie mit dem String
-Initialisierer hübsche Beschreibungen der Typnamen erhalten. Wie zum Beispiel print(String(describing: type(of: object)))
. Dabei kann object
eine Instanzvariable sein, wie ein Array, ein Wörterbuch, eine Int
, a NSDate
, eine Instanz einer benutzerdefinierten Klasse usw.
Hier ist meine vollständige Antwort: Liefert den Klassennamen des Objekts als String in Swift
Diese Frage sucht nach einem Weg, um den Klassennamen eines Objekts als String zu erhalten, aber ich schlug auch einen anderen Weg vor, um den Klassennamen einer Variablen zu erhalten, die keine Unterklasse von NSObject
ist. Hier ist es:
class Utility{
class func classNameAsString(obj: Any) -> String {
//prints more readable results for dictionaries, arrays, Int, etc
return String(describing: type(of: obj))
}
}
Ich habe eine statische Funktion erstellt, die ein Objekt vom Typ Any
als Parameter übernimmt und dessen Klassennamen als String
:) zurückgibt.
Ich habe diese Funktion mit einigen Variablen getestet:
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()
und die Ausgabe war:
Die oberste Antwort hat kein funktionierendes Beispiel für die neue Vorgehensweise mit type(of:
. Um Anfängern wie mir zu helfen, hier ist ein funktionierendes Beispiel, das größtenteils den Unterlagen von Apple entnommen wurde - 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'
Ich habe einige der anderen Antworten hier ausprobiert, aber der Kilometerstand scheint zu sehr auf dem zugrunde liegenden Objekt zu sein.
Ich habe jedoch einen Weg gefunden, wie Sie den Object-C-Klassennamen für ein Objekt erhalten können, indem Sie folgende Schritte ausführen:
now?.superclass as AnyObject! //replace now with the object you are trying to get the class name for
Hier ist ein Beispiel, wie Sie es verwenden würden:
let now = NSDate()
println("what is this = \(now?.superclass as AnyObject!)")
In diesem Fall wird NSDate in der Konsole gedruckt.
Ich habe diese Lösung gefunden, die hoffentlich für jemanden anderen funktionieren könnte. Bitte beachten Sie, dass dies nur für die Unterklasse NSObject funktioniert. Aber zumindest ist eine saubere und aufgeräumte Lösung.
class var className: String!{
let classString : String = NSStringFromClass(self.classForCoder())
return classString.componentsSeparatedByString(".").last;
}
Wenn Sie Cocoa (nicht CocoaTouch) verwenden, können Sie die className
-Eigenschaft für Objekte verwenden, die Unterklassen von NSObject sind.
println(now.className)
Diese Eigenschaft ist nicht für normale Swift-Objekte verfügbar, bei denen es sich nicht um Unterklassen von NSObject handelt (und tatsächlich gibt es in Swift keine Stamm-ID oder keinen Objekttyp).
class Person {
var name: String?
}
var p = Person()
println(person.className) // <- Compiler error
In CocoaTouch gibt es zurzeit keine Möglichkeit, eine Stringbeschreibung des Typs einer gegebenen Variablen abzurufen. Eine ähnliche Funktionalität ist für primitive Typen in Cocoa oder CocoaTouch nicht vorhanden.
Der Swift REPL kann eine Zusammenfassung der Werte einschließlich ihres Typs ausdrucken. Es ist daher möglich, dass diese Art der Introspektion zukünftig über eine API möglich ist.
EDIT:dump(object)
scheint den Trick auszuführen.
Im neuesten XCode 6.3 mit Swift 1.2 habe ich nur so gefunden:
if view.classForCoder.description() == "UISegment" {
...
}
Basierend auf den Antworten und Kommentaren, die Klass und Kevin Ballard oben gegeben haben, gehe ich mit:
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!)
was ausgedruckt wird:
"NSDate"
"ImplicitlyUnwrappedOptional"
"Optional"
"NSDate"
"NSString"
"PureSwiftClass"
"Int"
"Double"
Ich habe eine -Lösung für selbst entwickelte Klassen (oder solche, auf die Sie Zugriff haben) gefunden.
Fügen Sie die folgende berechnete Eigenschaft in Ihre Objektklassendefinition ein:
var className: String? {
return __FILE__.lastPathComponent.stringByDeletingPathExtension
}
Jetzt können Sie den Klassennamen auf Ihrem Objekt einfach wie folgt aufrufen:
myObject.className
Bitte beachten Sie, dass dies nur funktioniert, wenn Ihre Klassendefinition innerhalb einer Datei mit dem Namen genau gemacht wird, genau wie die Klasse, deren Name Sie wünschen.
Da dies häufig ist, sollte der Fall die obige Antwort für die meisten Fälle sein. Aber in einigen Sonderfällen müssen Sie möglicherweise eine andere Lösung finden.
Wenn Sie den Klassennamen innerhalb der Klasse (Datei) selbst benötigen, können Sie einfach diese Zeile verwenden:
let className = __FILE__.lastPathComponent.stringByDeletingPathExtension
Vielleicht hilft diese Methode einigen Leuten da draußen.
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)")
In lldb ab Beta 5 sehen Sie die Klasse eines Objekts mit dem Befehl:
fr v -d r shipDate
was gibt so etwas aus:
(DBSalesOrderShipDate_DBSalesOrderShipDate_ *) shipDate = 0x7f859940
Der Befehl erweitert bedeutet etwas wie:
Frame Variable
(eine Rahmenvariable drucken) -d run_target
(dynamische Typen erweitern)
Es ist nützlich zu wissen, dass die Verwendung von "Frame Variable" zur Ausgabe von Variablenwerten gewährleistet, dass kein Code ausgeführt wird.
Viele der Antworten funktionieren nicht mit dem neuesten Swift (Xcode 7.1.1 zum Zeitpunkt des Schreibens).
Der aktuelle Weg zum Abrufen der Informationen besteht darin, eine Mirror
zu erstellen und diese abzufragen. Für den Klassennamen ist es so einfach wie:
let mirror = Mirror(reflecting: instanceToInspect)
let classname:String = mirror.description
Zusätzliche Informationen zum Objekt können auch von der Mirror
abgerufen werden. Einzelheiten finden Sie unter http://swiftdoc.org/v2.1/type/Mirror/ .
Schnelle Version 4:
print("\(type(of: self)) ,\(#function)")
// within a function of a class
Danke @Joshua Dance
Es scheint keine generische Möglichkeit zu geben, den Typnamen des Typs eines beliebigen Werts zu drucken. Wie andere bereits erwähnt haben, können Sie fürKlasseInstanzen value.className
drucken, aber für primitive Werte scheint es, dass die Typinformationen zur Laufzeit verschwunden sind.
Zum Beispiel sieht es so aus, als gäbe es keine Möglichkeit, Folgendes einzugeben: 1.something()
und Int
für einen beliebigen Wert von something
. (Sie können als weitere Antwort vorschlagen, dass Sie i.bridgeToObjectiveC().className
verwenden, um Ihnen einen Hinweis zu geben, aber __NSCFNumber
ist nicht eigentlich der Typ von i
- genau das, in was es konvertiert wird, wenn es die Grenze einer Objective-C-Funktion überschreitet Anruf.)
Ich wäre froh, dass ich mich als falsch erwiesen habe, aber es sieht so aus, als ob die Typüberprüfung zur Kompilierzeit durchgeführt wird und wie C++ (mit deaktivierter RTTI) viele der Typinformationen zur Laufzeit weg sind.
So erhalten Sie eine Typzeichenfolge Ihres object oder Type , die konsistent ist, und berücksichtigt, zu welchem module die Objektdefinition gehört oder darin verschachtelt ist. Funktioniert 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"
Nicht genau das, was Sie suchen, aber Sie können den Typ der Variablen auch mit den Swift-Typen vergleichen:
let object: AnyObject = 1
if object is Int {
}
else if object is String {
}
Zum Beispiel.
Xcode 7.3.1, Swift 2.2:
String(instanceToPrint.self).componentsSeparatedByString(".").last
Swift 3.0, Xcode 8
Mit dem folgenden Code können Sie eine Instanz nach ihrer Klasse fragen. Sie können auch zwei Instanzen vergleichen, die dieselbe Klasse haben.
// 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")
}
Um einen Typ des Objekts zu erhalten oder Klasse des Objekts inSwift, müssen Sie einen Typ (von: yourObject) verwenden.
typ (von: IhrObjekt)
Bitte sehen Sie sich den Code-Snippet unten an und lassen Sie mich wissen, ob Sie nach etwas suchen oder nicht.
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)")
Dies ist auch hilfreich, wenn Sie prüfen, ob ein Objekt vom Typ einer Klasse ist:
if someObject is SomeClass {
//someObject is a type of SomeClass
}
Ein weiterer wichtiger Aspekt, der den Klassennamen beeinflusst, der von String(describing: type(of: self))
zurückgegeben wird, ist Access Control .
Betrachten Sie das folgende Beispiel basierend auf Swift 3.1.1 , Xcode 8.3.3 (Juli 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> { }
Wie Sie sehen, verfügen alle Klassen in diesem Beispiel über unterschiedliche Zugriffskontrollstufen, die sich auf ihre String
-Darstellung auswirken. Falls die Klassen über private
oder fileprivate
Zugriffskontrollebenen verfügen, scheint Swift eine Art Bezeichner anzuhängen, der sich auf die "nesting" -Klasse der betreffenden Klasse bezieht.
Das Ergebnis für PrivateDemoSystemCall
und FileprivateDemoSystemCall
ist, dass derselbe Bezeichner angehängt wird, da beide in derselben übergeordneten Klasse verschachtelt sind.
Ich habe bisher noch keinen Weg gefunden, um das loszuwerden, abgesehen von einigen hacky-Funktionen zum Ersetzen oder Regex.
Nur meine 2 Cent.