¿Cómo saber el tipo de un objeto (en Swift)?

255

Cuando se trata de entender un programa, o en algunos casos, es útil poder saber de qué tipo es algo. Sé que el depurador puede mostrarle cierta información de tipo, y generalmente puede confiar en la inferencia de tipo para salirse sin especificar el tipo en esas situaciones, pero aún así, realmente me gustaría tener algo como Pythontype()

dynamicType (ver esta pregunta )

Actualización: esto se ha cambiado en una versión reciente de Swift, obj.dynamicTypeahora le da una referencia al tipo y no a la instancia del tipo dinámico.

Este parece el más prometedor, pero hasta ahora no he podido averiguar el tipo real

class MyClass {
    var count = 0
}

let mc = MyClass()

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

También probé usando una referencia de clase para crear instancias de un objeto nuevo, lo que hace el trabajo, pero curiosamente me dio un error diciendo que debo añadir un requiredinicializador:

trabajos:

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

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

Sin embargo, solo un pequeño paso para descubrir realmente el tipo de cualquier objeto dado

editar : eliminé una cantidad considerable de detalles irrelevantes; mira el historial de edición si estás interesado :)

Jiaaro
fuente
1
posible duplicado de ¿Cómo obtener la clase de una variable en Swift?
David Berry
1
Curiosamente, print(mc)o dump(mc)imprimirá un resumen (que puede obtener de toString(mc)o reflect(mc).summary), que contendrá el nombre de la clase en alguna parte. Pero no está claro cómo obtener el nombre de la clase usted mismo.
newacct
@David es similar, pero no todas las variables son instancias de clase. Además, las preguntas se referían realmente a verificar si el tipo coincide con lo que el programador está buscando, mientras que espero descubrir el tipo al por mayor
Jiaaro

Respuestas:

284

Versión Swift 3:

type(of: yourObject)
Jérémy Lapointe
fuente
8
Hecho de la diversión. ¡Esto no funciona con opciones implícitas sin envolver! es decir var myVar: SomeType!. El compilador da el error "No se puede convertir el valor del tipo 'SomeType! .Type' (también conocido como 'ImplicitlyUnwrappedOptional <SomeType> .Type') al tipo de argumento esperado 'AnyClass' (también conocido como 'AnyObject.Type') El compilador sugiere agregar as! AnyClassdespués del tipo, pero luego programa se bloquea con un poco de "EXC_BAD_INSTRUCTION" y otro jiberrish que no puedo descifrar.
LightningStryk
1
De hecho, esta debería ser la respuesta aceptada ahora que Swift 3 existe. Gracias Jeremy!
biomiker
1
Si está buscando el nombre de tipo específico, cuando el tipo es de tipo de protocolo, esto puede no funcionar para usted.
Chris Prince
2
Si tiene un Stringque se pasa como tipo, Anyse type(of:)generará Any, no String.
ScottyBlades
1
@ScottyBlades, ¿cuál será la solución? ¿Puedes facilitar?
Centro comercial Mubin el
109

Swift 2.0:

La forma correcta de hacer este tipo de introspección de tipo sería con la estructura 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)

Luego, para acceder al tipo en sí desde la Mirrorestructura, usaría la propiedad de la siguiente subjectTypemanera:

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

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

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

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

Luego puede usar algo como esto:

    if anyMirror.subjectType == String.self {
        print("anyObject is a string!")
    } else {
        print("anyObject is not a string!")
    }
Gudbergur
fuente
77
Esto es genial. Tenga en cuenta que si el objeto que se refleja es de tipo opcional, la comparación con un tipo no opcional fallará. Stringy Optional(String)no son lo mismo
Thomas Verbeek,
1
Exactamente lo que estaba buscando, quería saber cuál es el tipo de objeto
Joseph
¿Hay algún tipo de comparación en este contexto que no fallará al comparar tipos opcionales con tipos no opcionales?
Chris Prince
Eso es lo que estaba buscando. Gracias @Gudbergur.
Centro comercial Mubin el
60

El dynamicType.printClassNamecódigo es de un ejemplo en el libro Swift. No sé de ninguna manera para tomar directamente un nombre de clase personalizado, pero puede verificar un tipo de instancias usando la ispalabra clave como se muestra a continuación. Este ejemplo también muestra cómo implementar una función className personalizada, si realmente desea el nombre de la clase como una cadena.

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: las
subclases de NSObjectya implementan su propia función className. Si está trabajando con Cocoa, puede usar esta propiedad.

class MyObj: NSObject {
    init() {
        super.init()
        println("My class is \(self.className)")
    }
}
MyObj()
Guión
fuente
2
Oye, no estoy seguro de cuándo cambió, pero como señaló Alex Pretzlav, el comportamiento ha cambiado.
Jiaaro
1
Si. A partir de Swift 3.0, subjectTypeya no está disponible y dynamicTypeprovoca un mensaje de desaprobación del compilador.
Raphael
41

A partir de Xcode 6.0.1 (al menos, no estoy seguro de cuándo lo agregaron), su ejemplo original ahora funciona:

class MyClass {
    var count = 0
}

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

Actualizar:

Para responder a la pregunta original, puede utilizar el tiempo de ejecución de Objective-C con objetos Swift simples con éxito.

Intenta lo siguiente:

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
fuente
Parece que lo cambiaron para darle el tipo en lugar de una instancia.
Jiaaro
@Jiaaro, actualicé mi respuesta con lo que creo que estabas buscando en tu pregunta original
Alex Pretzlav el
36

Si simplemente necesita verificar si la variable es de tipo X, o si se ajusta a algún protocolo, puede usarla is, o as?como se indica a continuación:

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

Esto es equivalente a isKindOfClassen Obj-C.

Y esto es equivalente 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>
}
Valerii Lider
fuente
La segunda parte de tu respuesta es incorrecta. La declaración 'if let' con as?lanzamiento condicional también hace lo mismo isKindOfClass, solo proporciona el resultado del lanzamiento si tiene éxito.
awolf
El equivalente de isMemberOfClasses la condición object.dynamicType == ClassName.self.
awolf
18

Swift 3:

if unknownType is MyClass {
   //unknownType is of class type MyClass
}
Peter
fuente
Creo que isexiste antes de Swift 3 ...?
Nicolas Miari
10

Para Swift 3.0

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

Para Swift 2.0 - 2.3

String(<Class-Name>)
techloverr
fuente
1
Lo importante de que esta respuesta sea correcta para mí es que la cadena resultante coincide exactamente con el nombre de la clase, por lo que puedo usar esto para obtener un nombre de entidad de datos centrales de una subclase NSManagedObject. Usé la versión Swift3.
Kendall Helmstetter Gelner
8

Aquí hay 2 formas en que recomiendo hacerlo:

if let thisShape = aShape as? Square 

O:

aShape.isKindOfClass(Square)

Aquí hay un ejemplo detallado:

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
fuente
2
print( aShape is Square ), el isoperador es más preferible.
DawnSong
Buena solución para mí para obtener el tipo de objetos.
nihasmata
1

Depende del caso de uso. Pero supongamos que desea hacer algo útil con sus tipos "variables". La switchdeclaración Swift es muy poderosa y puede ayudarlo a obtener los resultados que está buscando ...

    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
    }

En este caso, tenga un diccionario simple que contenga pares clave / valor que pueden ser UInt, Int o String. En el .filter()método en el diccionario, necesito asegurarme de probar los valores correctamente y solo probar una Cadena cuando es una cadena, etc. ¡La instrucción switch hace que esto sea simple y seguro! Al asignar 9 a la variable de tipo Any, se ejecuta el cambio para Int. Intenta cambiarlo a:

   let eValue:Any = "home9"

... e inténtalo de nuevo. Esta vez se ejecuta el as Stringcaso.

CPD
fuente
1

Si recibe una advertencia de "siempre verdadero / falla", es posible que deba enviar a Any antes de usar is

(foo as Any) is SomeClass
josef
fuente
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
fuente