Come scopri il tipo di un oggetto (in Swift)?


255

Quando si cerca di capire un programma, o in alcuni casi angolari, è utile poter effettivamente scoprire di che tipo è qualcosa. So che il debugger può mostrarti alcune informazioni sul tipo, e di solito puoi fare affidamento sull'inferenza del tipo per evitare di specificare il tipo in quelle situazioni, ma comunque, mi piacerebbe davvero avere qualcosa come Pythontype()

dynamicType (vedi questa domanda )

Aggiornamento: questo è stato modificato in una versione recente di Swift, obj.dynamicTypeora fornisce un riferimento al tipo e non all'istanza del tipo dinamico.

Questo sembra il più promettente, ma finora non sono stato in grado di scoprire il tipo reale

class MyClass {
    var count = 0
}

let mc = MyClass()

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

Ho anche provato ad utilizzare un riferimento di classe creare un'istanza di un nuovo oggetto, che fa il lavoro, ma stranamente mi ha dato un errore dicendo devo aggiungere un requiredinizializzatore:

lavori:

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

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

Tuttavia, resta solo un piccolo passo verso la scoperta del tipo di un determinato oggetto

modifica : ho rimosso un numero considerevole di dettagli ora irrilevanti - guarda la cronologia delle modifiche se sei interessato :)



1
È interessante notare che, print(mc)o dump(mc)stamperà un sommario (che si può ottenere da toString(mc)o reflect(mc).summary), che conterrà il nome della classe da qualche parte. Ma non è chiaro come ottenere solo il nome della classe.
Newacct,

@David simile, ma non tutte le variabili sono istanze di classe. Anche quelle domande riguardavano davvero il controllo se il tipo corrisponde a quello che il programmatore sta cercando, mentre spero solo di scoprire il tipo all'ingrosso
Jiaaro


Risposte:


284

Versione Swift 3:

type(of: yourObject)

8
Fatto divertente. Questo non funziona con gli optional implicitamente da scartare! vale a dire var myVar: SomeType!. Il compilatore fornisce l'errore "Impossibile convertire il valore di tipo 'SomeType! .Type' (aka 'ImplicitlyUnwrappedOptional <SomeType> .Type') nel tipo di argomento previsto 'AnyClass' (aka 'AnyObject.Type') Il compilatore suggerisce di aggiungere as! AnyClassdopo il tipo ma poi il programma si arresta in modo anomalo con "EXC_BAD_INSTRUCTION" e altri jiberrish che non riesco a decifrare.
LightningStryk

1
In effetti, questa dovrebbe essere la risposta accettata ora che Swift 3 esiste. Grazie Jeremy!
biomiker,

1
Se stai cercando il nome specifico del tipo, quando il tipo è di tipo protocollo, questo potrebbe non funzionare per te.
Chris Prince,

2
Se si dispone di una Stringche viene passato come tipo Anypoi type(of:)uscita volontà Any, non è String.
ScottyBlades,

1
@ScottyBlades quindi quale sarà la soluzione. Puoi fornire?
Mubin Mall,

109

Swift 2.0:

Il modo corretto di fare questo tipo di introspezione di tipo sarebbe con la struttura Mirror ,

    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)

Quindi per accedere al tipo stesso dalla Mirrorstruttura dovresti usare la proprietà in questo subjectTypemodo:

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

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

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

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

È quindi possibile utilizzare qualcosa del genere:

    if anyMirror.subjectType == String.self {
        print("anyObject is a string!")
    } else {
        print("anyObject is not a string!")
    }

7
Questo è fantastico Fare attenzione che se l'oggetto in fase di mirroring è di tipo facoltativo, il confronto con un tipo non opzionale avrà esito negativo. Stringe Optional(String)non sono gli stessi.
Thomas Verbeek,

1
Esattamente quello che stavo cercando, volevo sapere qual è il tipo di oggetto
Joseph,

Esiste un tipo di confronto in questo contesto che non fallirà nel confronto tra tipi opzionali e non opzionali?
Chris Prince,

Questo è quello che stavo cercando. Grazie @Gudbergur.
Mubin Mall,

60

Il dynamicType.printClassNamecodice proviene da un esempio nel libro Swift. Non so per quale motivo afferrare direttamente un nome di classe personalizzato, ma puoi controllare un tipo di istanza usando la isparola chiave come mostrato di seguito. Questo esempio mostra anche come implementare una funzione className personalizzata, se si desidera davvero il nome della classe come stringa.

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

Nota:
che le sottoclassi di NSObjectimplementano già la propria funzione className. Se lavori con Cocoa, puoi semplicemente usare questa proprietà.

class MyObj: NSObject {
    init() {
        super.init()
        println("My class is \(self.className)")
    }
}
MyObj()

2
Ehi, non sono sicuro di quando sia cambiato, ma come ha sottolineato Alex Pretzlav, il comportamento è cambiato.
Jiaaro,

1
Sì. A partire da Swift 3.0, subjectTypenon è più disponibile e dynamicTypecausa un messaggio di deprecazione dal compilatore.
Raffaello,

41

A partire da Xcode 6.0.1 (almeno, non sono sicuro quando lo hanno aggiunto), il tuo esempio originale ora funziona:

class MyClass {
    var count = 0
}

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

Aggiornare:

Per rispondere alla domanda originale, puoi effettivamente utilizzare correttamente il runtime Objective-C con semplici oggetti Swift.

Prova quanto segue:

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))

Sembra che lo abbiano cambiato per darti il ​​tipo invece di un'istanza.
Jiaaro,

@Jiaaro, ho aggiornato la mia risposta con quello che penso che stavi cercando nella tua domanda originale
Alex Pretzlav

36

Se hai semplicemente bisogno di verificare se la variabile è di tipo X o se è conforme ad un protocollo, puoi usare is, o as?come nel seguito:

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

Questo è equivalente a isKindOfClassin Obj-C.

E questo equivale a conformsToProtocol, oisMemberOfClass

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

La seconda parte della tua risposta è sbagliata. Anche l'istruzione 'if let' con il as?cast condizionale fa lo stesso isKindOfClass, fornisce anche il risultato del cast in caso di successo.
Awolf

L'equivalente di isMemberOfClassè la condizione object.dynamicType == ClassName.self.
Awolf

18

Swift 3:

if unknownType is MyClass {
   //unknownType is of class type MyClass
}

Penso che isesista da prima di Swift 3 ...?
Nicolas Miari,

10

Per Swift 3.0

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

Per Swift 2.0 - 2.3

String(<Class-Name>)

1
L'importante è che questa risposta sia corretta per me, è che la stringa risultante corrisponde esattamente al nome della classe, quindi posso usarla per ottenere un nome di entità Core Data da una sottoclasse NSManagedObject. Ho usato la versione Swift3.
Kendall Helmstetter Gelner,

8

Ecco 2 modi in cui consiglio di farlo:

if let thisShape = aShape as? Square 

O:

aShape.isKindOfClass(Square)

Ecco un esempio dettagliato:

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

2
print( aShape is Square ), l' isoperatore è più preferibile.
DawnSong

Buona soluzione per me per ottenere il tipo di oggetti.
Nihasmata,

1

Dipende dal caso d'uso. Ma supponiamo che tu voglia fare qualcosa di utile con i tuoi tipi "variabili". L' switchaffermazione Swift è molto potente e può aiutarti a ottenere i risultati che stai cercando ...

    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 questo caso, disporre di un dizionario semplice che contiene coppie chiave / valore che possono essere UInt, Int o String. Nel .filter()metodo sul dizionario, devo assicurarmi di testare i valori correttamente e testare una stringa solo quando è una stringa, ecc. L'istruzione switch lo rende semplice e sicuro! Assegnando 9 alla variabile di tipo Any, esegue lo switch per Int. Prova a cambiarlo in:

   let eValue:Any = "home9"

..e riprovare. Questa volta esegue il as Stringcaso.


1

Se ricevi un avviso "sempre vero / non riuscito", potresti dover eseguire il cast su Qualsiasi prima di utilizzarlo is

(foo as Any) is SomeClass

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.