web-dev-qa-db-de.com

Wie drucke ich den Typ oder die Klasse einer Variablen in Swift?

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.

313
Matt Bridges

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 .

350
Klaas

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 .

45
akashivskyy

Ist es das, wonach du suchst?

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

Es druckt "__NSDate"

UPDATE: Bitte beachten Sie, dass dies ab Beta05 nicht mehr funktioniert

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

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.
23
Matt Hagen

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"
18
kiding

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
17
Leandros

Sie können reflektieren verwenden, um Informationen zum Objekt zu erhalten.
Zum Beispiel Name der Objektklasse:

 var classname = reflect (jetzt) ​​.summary 
16
Stan

Ich hatte Glück mit:

let className = NSStringFromClass(obj.dynamicType)
13
mxcl

Xcode 8 Swift 3.0 Verwendungstyp (von :)

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

In Xcode 8, Swift 3.0

Schritte:

1. Holen Sie sich den Typ:

Option 1:

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

Option 2:

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

2. Typ in String konvertieren:

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

In Swift 3.0 können Sie type(of:) verwenden, da das dynamicType-Schlüsselwort entfernt wurde.

8
BrunoSerrano

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 objecteine 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:

  • diccionary ist vom Typ Dictionary
  • array ist vom Typ Array
  • numInt ist vom Typ Int
  • numFloat ist vom Typ CGFloat
  • numDouble ist vom Typ Double
  • classOne ist vom Typ: ClassOne
  • classTwo ist vom Typ: ClassTwo
  • jetzt ist vom Typ: Datum
  • lbl ist vom Typ: UILabel
6
mauricioconde

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'
5
Joshua Dance

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.

4
James Jones

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;
}
4
DennyLou

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.

4
Matt Bridges

Im neuesten XCode 6.3 mit Swift 1.2 habe ich nur so gefunden:

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

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

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.

3
Dschee
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

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/ .

3
Joseph Lord

Schnelle Version 4:

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

Danke @Joshua Dance

2
dengApro

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.

2
ipmcc

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
2
Igor Muzyka

Schnell 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
}

Verwendungszweck:

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

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.

1

Xcode 7.3.1, Swift 2.2:

String(instanceToPrint.self).componentsSeparatedByString(".").last

1
leanne

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")
}
1
LukeSideWalker

Um einen Typ des Objekts zu erhalten oder Klasse des Objekts inSwift, müssen Sie einen Typ (von: yourObject) verwenden. 

typ (von: IhrObjekt)

1
Rahul Panzade

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)")
0
DILIP KOSURI

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
}
0
Nam

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.

0
Philipp Jahoda