Wie finden Sie den Typ eines Objekts heraus (in Swift)?

255

Wenn Sie versuchen, ein Programm zu verstehen, oder in einigen Fällen, ist es hilfreich, tatsächlich herauszufinden, um welchen Typ es sich handelt. Ich weiß, dass der Debugger Ihnen einige Typinformationen anzeigen kann, und Sie können sich normalerweise auf Typinferenz verlassen, um nicht den Typ in solchen Situationen anzugeben, aber dennoch hätte ich wirklich gerne etwas wie Pythonstype()

dynamicType (siehe diese Frage )

Update: Dies wurde in einer neueren Version von Swift geändert und enthält obj.dynamicTypenun einen Verweis auf den Typ und nicht auf die Instanz des dynamischen Typs.

Dieser scheint der vielversprechendste zu sein, aber bisher konnte ich den tatsächlichen Typ nicht herausfinden

class MyClass {
    var count = 0
}

let mc = MyClass()

# update: this now evaluates as true
mc.dynamicType === MyClass.self

Ich habe auch versucht, eine Klassenreferenz zu verwenden, um ein neues Objekt zu instanziieren, was funktioniert , aber seltsamerweise habe ich einen Fehler erhalten, der besagt, dass ich einen requiredInitialisierer hinzufügen muss :

funktioniert:

class MyClass {
    var count = 0
    required init() {
    }
}

let myClass2 = MyClass.self
let mc2 = MyClass2()

Trotzdem nur ein kleiner Schritt, um den Typ eines bestimmten Objekts tatsächlich zu entdecken

Bearbeiten : Ich habe eine beträchtliche Anzahl von jetzt irrelevanten Details entfernt - schauen Sie sich den Bearbeitungsverlauf an, wenn Sie interessiert sind :)

Jiaaro
quelle
1
mögliches Duplikat von Wie erhalte ich die Klasse einer Variablen in Swift?
David Berry
1
Interessanterweise print(mc)oder dump(mc)druckt eine Zusammenfassung (die Sie von toString(mc)oder erhalten können reflect(mc).summary), die irgendwo den Klassennamen enthält. Es ist jedoch nicht klar, wie Sie selbst nur den Klassennamen erhalten.
Newacct
@ David ähnlich, aber nicht alle Variablen sind Klasseninstanzen. Bei diesen Fragen ging es auch wirklich darum zu überprüfen, ob der Typ dem entspricht, wonach der Programmierer sucht, während ich hoffe, nur den
Typgroßhandel
Mögliches Duplikat von So überprüfen Sie, ob zwei Instanzen in swift
holex am

Antworten:

284

Swift 3 Version:

type(of: yourObject)
Jérémy Lapointe
quelle
8
Lustige Tatsache. Dies funktioniert nicht mit implizit ausgepackten Optionen! dh var myVar: SomeType!. Der Compiler gibt den Fehler "Der Wert vom Typ 'SomeType! .Type' (auch bekannt als 'ImplicitlyUnwrappedOptional <SomeType> .Type') kann nicht in den erwarteten Argumenttyp 'AnyClass' (auch bekannt als 'AnyObject.Type') konvertiert werden. Der Compiler schlägt jedoch vor, as! AnyClassnach dem Typ hinzuzufügen Programm stürzt mit einigen "EXC_BAD_INSTRUCTION" und anderen Jiberrish ab, die ich nicht entziffern kann.
LightningStryk
1
In der Tat sollte dies die akzeptierte Antwort sein, sobald Swift 3 existiert. Vielen Dank, Jeremy!
Biomiker
1
Wenn Sie nach einem bestimmten Typnamen suchen und der Typ ein Protokolltyp ist, funktioniert dies möglicherweise nicht für Sie.
Chris Prince
2
Wenn Sie ein , Stringdie als Typ geleitet wird Anydann type(of:)ausgeben wird Any, nicht String.
ScottyBlades
1
@ ScottyBlades also was wird die Lösung sein. Können Sie zur Verfügung stellen?
Mubin Mall
109

Swift 2.0:

Der richtige Weg, um diese Art der Typ-Selbstbeobachtung durchzuführen , wäre die Mirror-Struktur .

    let stringObject:String = "testing"
    let stringArrayObject:[String] = ["one", "two"]
    let viewObject = UIView()
    let anyObject:Any = "testing"

    let stringMirror = Mirror(reflecting: stringObject)
    let stringArrayMirror = Mirror(reflecting: stringArrayObject)
    let viewMirror = Mirror(reflecting: viewObject)
    let anyMirror = Mirror(reflecting: anyObject)

MirrorUm dann über die Struktur auf den Typ selbst zuzugreifen, verwenden Sie die Eigenschaft subjectTypewie folgt:

    // Prints "String"
    print(stringMirror.subjectType)

    // Prints "Array<String>"
    print(stringArrayMirror.subjectType)

    // Prints "UIView"
    print(viewMirror.subjectType)

    // Prints "String"
    print(anyMirror.subjectType)

Sie können dann so etwas verwenden:

    if anyMirror.subjectType == String.self {
        print("anyObject is a string!")
    } else {
        print("anyObject is not a string!")
    }
Gudbergur
quelle
7
Das ist toll. Beachten Sie, dass der Vergleich mit einem nicht optionalen Typ fehlschlägt, wenn das gespiegelte Objekt vom optionalen Typ ist. Stringund Optional(String)sind nicht gleich.
Thomas Verbeek
1
Genau das, wonach ich gesucht habe, wollte wissen, was der Typ des Objekts ist
Joseph
Gibt es in diesem Zusammenhang einen Vergleichstyp , der beim Vergleich von optionalen mit nicht optionalen Typen nicht fehlschlägt ?
Chris Prince
Das habe ich gesucht. Danke @Gudbergur.
Mubin Mall
60

Der dynamicType.printClassNameCode stammt aus einem Beispiel im Swift-Buch. Es gibt keine Möglichkeit, einen benutzerdefinierten Klassennamen direkt abzurufen, aber Sie können einen Instanztyp mithilfe des isunten gezeigten Schlüsselworts überprüfen . Dieses Beispiel zeigt auch, wie eine benutzerdefinierte className-Funktion implementiert wird, wenn der Klassenname wirklich als Zeichenfolge verwendet werden soll.

class Shape {
    class func className() -> String {
        return "Shape"
    }
}

class Square: Shape {
    override class func className() -> String {
        return "Square"
    }
}

class Circle: Shape {
    override class func className() -> String {
        return "Circle"
    }
}

func getShape() -> Shape {
    return Square() // hardcoded for example
}

let newShape: Shape = getShape()
newShape is Square // true
newShape is Circle // false
newShape.dynamicType.className() // "Square"
newShape.dynamicType.className() == Square.className() // true

Hinweis: Diese
Unterklassen NSObjectimplementieren bereits ihre eigene className-Funktion. Wenn Sie mit Kakao arbeiten, können Sie diese Eigenschaft einfach verwenden.

class MyObj: NSObject {
    init() {
        super.init()
        println("My class is \(self.className)")
    }
}
MyObj()
Strich
quelle
2
Hey, ich bin mir nicht sicher, wann es sich geändert hat, aber wie Alex Pretzlav betonte, hat sich das Verhalten geändert.
Jiaaro
1
Ja. Ab Swift 3.0 subjectTypeist es nicht mehr verfügbar und dynamicTypeverursacht eine Verfallsmeldung vom Compiler.
Raphael
41

Ab Xcode 6.0.1 (zumindest nicht sicher, wann sie hinzugefügt wurden) funktioniert Ihr ursprüngliches Beispiel jetzt:

class MyClass {
    var count = 0
}

let mc = MyClass()
mc.dynamicType === MyClass.self // returns `true`

Aktualisieren:

Um die ursprüngliche Frage zu beantworten, können Sie die Objective-C-Laufzeit tatsächlich erfolgreich mit einfachen Swift-Objekten verwenden.

Versuche Folgendes:

import Foundation
class MyClass { }
class SubClass: MyClass { }

let mc = MyClass()
let m2 = SubClass()

// Both of these return .Some("__lldb_expr_35.SubClass"), which is the fully mangled class name from the playground
String.fromCString(class_getName(m2.dynamicType))
String.fromCString(object_getClassName(m2))
// Returns .Some("__lldb_expr_42.MyClass")
String.fromCString(object_getClassName(mc))
Alex Pretzlav
quelle
Sieht so aus, als hätten sie es geändert, um Ihnen den Typ anstelle einer Instanz zu geben.
Jiaaro
@ Jiaaro, ich habe meine Antwort mit dem aktualisiert, was ich denke, dass Sie in Ihrer ursprünglichen Frage
gesucht haben
36

Wenn Sie einfach nur überprüfen müssen , ob die Variable vom Typ X sind, oder dass es bis zu einem gewissen Protokoll entspricht, dann können Sie verwenden is, oder , as?wie im Folgenden:

var unknownTypeVariable =if unknownTypeVariable is <ClassName> {
    //the variable is of type <ClassName>
} else {
    //variable is not of type <ClassName>
}

Dies entspricht dem isKindOfClassin Obj-C.

Und das ist gleichbedeutend mit conformsToProtocoloderisMemberOfClass

var unknownTypeVariable =if let myClass = unknownTypeVariable as? <ClassName or ProtocolName> {
    //unknownTypeVarible is of type <ClassName or ProtocolName>
} else {
    //unknownTypeVariable is not of type <ClassName or ProtocolName>
}
Valerii Lider
quelle
Der zweite Teil Ihrer Antwort ist falsch. Die 'if let'-Anweisung mit as?bedingter Besetzung macht dasselbe wie isKindOfClassauch, liefert nur das Ergebnis der Besetzung, falls sie erfolgreich sein sollte.
Awolf
Das Äquivalent von isMemberOfClassist die Bedingung object.dynamicType == ClassName.self.
Awolf
18

Swift 3:

if unknownType is MyClass {
   //unknownType is of class type MyClass
}
Peter
quelle
Ich denke, isexistiert vor Swift 3 ...?
Nicolas Miari
10

Für Swift 3.0

String(describing: <Class-Name>.self)

Für Swift 2.0 - 2.3

String(<Class-Name>)
techloverr
quelle
1
Das Wichtige daran, dass diese Antwort für mich richtig ist, ist, dass die resultierende Zeichenfolge genau mit dem Klassennamen übereinstimmt. Daher kann ich damit einen Core Data-Entitätsnamen aus einer NSManagedObject-Unterklasse abrufen. Ich habe die Swift3-Version verwendet.
Kendall Helmstetter Gelner
8

Hier sind zwei Möglichkeiten, die ich empfehle:

if let thisShape = aShape as? Square 

Oder:

aShape.isKindOfClass(Square)

Hier ist ein detailliertes Beispiel:

class Shape { }
class Square: Shape { } 
class Circle: Shape { }

var aShape = Shape()
aShape = Square()

if let thisShape = aShape as? Square {
    println("Its a square")
} else {
    println("Its not a square")
}

if aShape.isKindOfClass(Square) {
    println("Its a square")
} else {
    println("Its not a square")
}
Esqarrouth
quelle
2
print( aShape is Square ), isBetreiber ist stärker bevorzugt.
DawnSong
Gute Lösung für mich, um die Art der Objekte zu bekommen.
Nihasmata
1

Kommt auf den Anwendungsfall an. Nehmen wir jedoch an, Sie möchten mit Ihren "variablen" Typen etwas Nützliches tun. Die Swift- switchAussage ist sehr mächtig und kann Ihnen helfen, die gewünschten Ergebnisse zu erzielen ...

    let dd2 = ["x" : 9, "y" : "home9"]
    let dds = dd2.filter {
        let eIndex = "x"
        let eValue:Any = 9
        var r = false

        switch eValue {
        case let testString as String:
            r = $1 == testString
        case let testUInt as UInt:
            r = $1 == testUInt
        case let testInt as Int:
            r = $1 == testInt
        default:
            r = false
        }

        return r && $0 == eIndex
    }

In diesem Fall verfügen Sie über ein einfaches Wörterbuch, das Schlüssel / Wert-Paare enthält, die UInt, Int oder String sein können. Bei der .filter()Methode im Wörterbuch muss ich sicherstellen, dass ich die Werte korrekt teste und nur dann auf einen String teste, wenn es sich um einen String usw. handelt. Die switch-Anweisung macht dies einfach und sicher! Durch Zuweisen von 9 zur Variablen vom Typ Any wird der Schalter für Int ausgeführt. Versuchen Sie es zu ändern in:

   let eValue:Any = "home9"

..und versuchen Sie es erneut. Diesmal wird der as StringFall ausgeführt.

CPD
quelle
1

Wenn Sie die Warnung "Immer wahr / fehlgeschlagen" erhalten, müssen Sie diese möglicherweise vor der Verwendung in "Beliebig" umwandeln is

(foo as Any) is SomeClass
josef
quelle
0
//: Playground - noun: a place where people can play

import UIKit

class A {
    class func a() {
        print("yeah")
    }

    func getInnerValue() {
        self.dynamicType.a()
    }
}

class B: A {
    override class func a() {
        print("yeah yeah")
    }
}

B.a() // yeah yeah
A.a() // yeah
B().getInnerValue() // yeah yeah
A().getInnerValue() // yeah
Avi Cohen
quelle