Comprobar si un objeto es un tipo dado en Swift

267

Tengo una matriz compuesta de AnyObject. Quiero iterar sobre él y encontrar todos los elementos que son instancias de matriz.

¿Cómo puedo verificar si un objeto es de un tipo dado en Swift?

Encore PTL
fuente
Su pregunta se refiere a encontrar el tipo de un objeto dado, pero ha aceptado una respuesta que solo es capaz de verificar si un objeto es de un tipo dado. Le sugiero que edite su pregunta específicamente para que, de lo contrario, muchos lectores no estarán satisfechos con la respuesta que ha aceptado. (Todas las otras respuestas son similares, así que afortunadamente no necesita preocuparse por invalidarlas al limitar su pregunta).
Jeremy Banks
He editado esta pregunta para desambiguarla de stackoverflow.com/q/24093433 , que estoy votando para reabrir. Ambas son preguntas útiles, similares, pero las respuestas son bastante distintas, por lo que sería útil mantenerlas separadas.
Jeremy Banks
1
posible duplicado de ¿Cómo saber el tipo de un objeto (en Swift)?
Esqarrouth

Respuestas:

304

Si desea verificar un tipo específico, puede hacer lo siguiente:

if let stringArray = obj as? [String] {
    // obj is a string array. Do something with stringArray
}
else {
    // obj is not a string array
}

Puedes usar "como!" y eso arrojará un error de tiempo de ejecución si objno es de tipo[String]

let stringArray = obj as! [String]

También puede verificar un elemento a la vez:

let items : [Any] = ["Hello", "World"]
for obj in items {
   if let str = obj as? String {
      // obj is a String. Do something with str
   }
   else {
      // obj is not a String
   }
}
Drewag
fuente
¿Por qué eso solo arrojaría un error de tiempo de ejecución y no un error de tiempo de compilación cuando ?no está presente? Suena como asy ?cuando se combina realizará una verificación de tiempo de ejecución. ¿Cuándo sería apropiado usar assin ?? Gracias por adelantado.
Unheilig
@Unheilig Solo debe usar assin el ?si no hay forma de que su programa pueda recuperarse del objeto que no es de ese tipo porque el programa se detendrá inmediatamente si no lo es. El uso de ?en la ifdeclaración permite que el programa continúe.
drewag
Gracias por responder. Corríjame si me equivoco: pensé que usar el ?en este caso realizaría una verificación de tipo "genérico", en caso afirmativo, a la cláusula if, si no, a la cláusula else. Sin lo ?contrario, nunca se ingresaría y, como señaló, provocará un error de tiempo de ejecución. Gracias de nuevo.
Unheilig
@Unheilig Lo siento, no entiendo lo que estás diciendo / preguntando. El ?permite la asignación para volver nilhaciendo que la sentencia if para el retorno falsey, por tanto, que cae a través de la sentencia else. Sin embargo, creo que la explicación ayuda con el entendimiento, pero if leten realidad es un caso especial en el compilador
DREWAG
1
@Unheilig Correcto, puede usar var si desea poder modificar el valor mientras está en ese ámbito local (esos cambios no afectarán fuera del ámbito)
drewag
202

En Swift 2.2 - 5 ahora puede hacer:

if object is String
{
}

Luego, para filtrar su matriz:

let filteredArray = originalArray.filter({ $0 is Array })

Si tiene varios tipos para verificar:

    switch object
    {
    case is String:
        ...

    case is OtherClass:
        ...

    default:
        ...
    }
el significado importa
fuente
Esta solución es más corta, pero tiene una desventaja: no se puede usar objectcomo Stringdentro de las llaves (al menos en Swift 2), mientras que con la letsolución puede hacerlo.
Ferran Maylinch
@FerranMaylinch No entiendo lo que quieres decir porque usar objecten el bloque está bien.
significado-asuntos
@ significado-importa, por ejemplo, no podrá hacerlo object.uppercaseStringporque el tipo de la variable no está fundido a ese tipo, simplemente verificó que el objeto (señalado por la variable) es unString
Ferran Maylinch
¿Cómo puede hacer esto si el tipo de clase que está buscando es arbitrario? Si solo tiene una variable, ¿necesita obtener un tipo de clase?
Alex Zavatone
152

Si solo desea saber si un objeto es un subtipo de un tipo dado, entonces hay un enfoque más simple:

class Shape {}
class Circle : Shape {}
class Rectangle : Shape {}

func area (shape: Shape) -> Double {
  if shape is Circle { ... }
  else if shape is Rectangle { ... }
}

“Utilice el operador de verificación de tipo (is) para verificar si una instancia es de cierto tipo de subclase. El operador de verificación de tipo devuelve verdadero si la instancia es de ese tipo de subclase y falso si no lo es ". Extracto de: Apple Inc. "El lenguaje de programación Swift". iBooks .

En lo anterior, la frase 'de cierto tipo de subclase' es importante. El compilador acepta el uso de is Circley is Rectangleporque ese valor shapese declara como Shape(una superclase de Circley Rectangle).

Si está utilizando tipos primitivos, la superclase sería Any. Aquí hay un ejemplo:

 21> func test (obj:Any) -> String {
 22.     if obj is Int { return "Int" }
 23.     else if obj is String { return "String" }
 24.     else { return "Any" }
 25. } 
 ...  
 30> test (1)
$R16: String = "Int"
 31> test ("abc")
$R17: String = "String"
 32> test (nil)
$R18: String = "Any"
GoZoner
fuente
2
¿Qué sucede si almaceno un tipo primitivo en una matriz o si la matriz es del tipo primitivo, istodavía funcionaría aquí? Gracias.
Unheilig
Debería funcionar si declaras el objectcomo Any. Actualizado con un ejemplo.
GoZoner
Gracias por responder. Se ve prometedor. Mi única duda es que, según la respuesta a continuación, en la que AnyObjectse sugiere, parece haber sido replicada debido a que AnyObjectno heredó de NSObject. Si Anyes diferente, entonces esta sería una gran solución también. Gracias.
Unheilig
21

Tengo 2 formas de 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")
}

Editar: 3 ahora:

let myShape = Shape()
if myShape is Shape {
    print("yes it is")
}
Esqarrouth
fuente
1
isKindOfClasses un método del NSObjectprotocolo; solo debería funcionar para las clases que lo adoptan (todas las clases que descienden de NSObject, más cualquier clase personalizada de Swift que lo adopte explícitamente)
Nicolas Miari
16

para swift4:

if obj is MyClass{
    // then object type is MyClass Type
}
Ahmad Labeeb
fuente
mejor respuesta para mí
ruselli
9

Asumir dibujar Triángulo es una instancia de UIView. Para verificar si drawTriangle es del tipo UITableView:

En Swift 3 ,

if drawTriangle is UITableView{
    // in deed drawTriangle is UIView
    // do something here...
} else{
    // do something here...
}

Esto también podría usarse para clases definidas por usted mismo. Puede usar esto para verificar las subvistas de una vista.

emmmphd
fuente
5

¿Por qué no utilizar la funcionalidad integrada creada especialmente para esta tarea?

let myArray: [Any] = ["easy", "as", "that"]
let type = type(of: myArray)

Result: "Array<Any>"
Patrik Forsberg
fuente
La función type () es simple
:)
5

Ten cuidado con esto:

var string = "Hello" as NSString
var obj1:AnyObject = string
var obj2:NSObject = string

print(obj1 is NSString)
print(obj2 is NSString)
print(obj1 is String)
print(obj2 is String) 

Las cuatro últimas líneas devuelven verdadero, esto se debe a que si escribe

var r1:CGRect = CGRect()
print(r1 is String)

... imprime "falso", por supuesto, pero una Advertencia dice que falla la transmisión de CGRect a String. Por lo tanto, se conectan algunos tipos, y la palabra clave 'is' llama un molde implícito.

Deberías usar uno de estos:

myObject.isKind(of: MyClass.self)) 
myObject.isMember(of: MyClass.self))
tontonCD
fuente
2

Si solo desea verificar la clase sin recibir una advertencia debido al valor definido no utilizado (let someVariable ...), simplemente puede reemplazar el material let con un valor booleano:

if (yourObject as? ClassToCompareWith) != nil {
   // do what you have to do
}
else {
   // do something else
}

Xcode propuso esto cuando utilicé let way y no utilicé el valor definido.

the_mariooo
fuente
2

¿Por qué no usar algo como esto?

fileprivate enum types {
    case typeString
    case typeInt
    case typeDouble
    case typeUnknown
}

fileprivate func typeOfAny(variable: Any) -> types {
    if variable is String {return types.typeString}
    if variable is Int {return types.typeInt}
    if variable is Double {return types.typeDouble}
    return types.typeUnknown
}

en Swift 3.

Dawy
fuente
2

Swift 4.2, en mi caso, usando la función isKind.

isKind (of :) Devuelve un valor booleano que indica si el receptor es una instancia de una clase dada o una instancia de cualquier clase que herede de esa clase.

  let items : [AnyObject] = ["A", "B" , ... ]
  for obj in items {
    if(obj.isKind(of: NSString.self)){
      print("String")
    }
  }

Leer más https://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418511-iskind

Tung Tran
fuente
1
Eso no es rápido. Es Cocoa y funciona solo donde funcionaría para el Objetivo C.
mate
1

myObject as? Stringdevuelve nilsi myObjectno es a String. De lo contrario, devuelve un String?, para que pueda acceder a la cadena en sí myObject!o emitirla de forma myObject! as Stringsegura.

cprcrack
fuente
1

Swift 3:

class Shape {}
class Circle : Shape {}
class Rectangle : Shape {}

if aShape.isKind(of: Circle.self) {
}
Harris
fuente
1

Solo en aras de la exhaustividad basada en la respuesta aceptada y algunas otras:

let items : [Any] = ["Hello", "World", 1]

for obj in items where obj is String {
   // obj is a String. Do something with str
}

Pero también puede ( compactMaptambién "asigna" los valores que filterno):

items.compactMap { $0 as? String }.forEach{ /* do something with $0 */ ) }

Y una versión usando switch:

for obj in items {
    switch (obj) {
        case is Int:
           // it's an integer
        case let stringObj as String:
           // you can do something with stringObj which is a String
        default:
           print("\(type(of: obj))") // get the type
    }
}

Pero atendiendo a la pregunta, para verificar si es una matriz (es decir [String]):

let items : [Any] = ["Hello", "World", 1, ["Hello", "World", "of", "Arrays"]]

for obj in items {
  if let stringArray = obj as? [String] {
    print("\(stringArray)")
  }
}

O de manera más general (vea esta otra pregunta respuesta )

for obj in items {
  if obj is [Any] {
    print("is [Any]")
  }

  if obj is [AnyObject] {
    print("is [AnyObject]")
  }

  if obj is NSArray {
    print("is NSArray")
  }
}
FranMowinckel
fuente
1

as?no siempre le dará el resultado esperado porque asno prueba si un tipo de datos es de un tipo específico, sino solo si un tipo de datos se puede convertir o representar como un tipo específico.

Considere este código, por ejemplo:

func handleError ( error: Error ) {
    if let nsError = error as? NSError {

Cada tipo de datos conforme al Errorprotocolo se puede convertir en un NSErrorobjeto, por lo que esto siempre tendrá éxito . Sin embargo, eso no significa que errorde hecho sea un NSErrorobjeto o una subclase de él.

Una verificación de tipo correcta sería:

func handleError ( error: Error ) {
    if type(of: error) == NSError.self {

Sin embargo, esto verifica solo el tipo exacto. Si desea incluir también la subclase de NSError, debe usar:

func handleError ( error: Error ) {
    if error is NSError.Type {
Mecki
fuente
0

Si tiene una respuesta como esta:

{
  "registeration_method": "email",
  "is_stucked": true,
  "individual": {
    "id": 24099,
    "first_name": "ahmad",
    "last_name": "zozoz",
    "email": null,
    "mobile_number": null,
    "confirmed": false,
    "avatar": "http://abc-abc-xyz.amazonaws.com/images/placeholder-profile.png",
    "doctor_request_status": 0
  },
  "max_number_of_confirmation_trials": 4,
  "max_number_of_invalid_confirmation_trials": 12
}

y desea verificar el valor is_stuckedque se leerá como AnyObject, todo lo que tiene que hacer es esto

if let isStucked = response["is_stucked"] as? Bool{
  if isStucked{
      print("is Stucked")
  }
  else{
      print("Not Stucked")
 }
}
Abo3atef
fuente
0

Si no sabe que obtendrá una matriz de diccionarios o un solo diccionario en la respuesta del servidor, debe verificar si el resultado contiene una matriz o no.
En mi caso, siempre recibo una serie de diccionarios, excepto una vez. Entonces, para manejar eso, utilicé el siguiente código para Swift 3.

if let str = strDict["item"] as? Array<Any>

¿Aquí como? La matriz verifica si el valor obtenido es matriz (de elementos del diccionario). En otro caso, puede manejar si es un elemento de diccionario único que no se mantiene dentro de una matriz.

VS
fuente
0

Swift 5.2 y Versión Xcode: 11.3.1 (11C504)

Aquí está mi solución de verificar el tipo de datos:

 if let typeCheck = myResult as? [String : Any] {
        print("It's Dictionary.")
    } else { 
        print("It's not Dictionary.") 
    }

Espero que te ayude.

Spencer Reid
fuente
Al responder una pregunta anterior, su respuesta sería mucho más útil para otros usuarios de StackOverflow si incluyera algún contexto para explicar cómo ayuda su respuesta, particularmente para una pregunta que ya tiene una respuesta aceptada. Ver: ¿Cómo escribo una buena respuesta ?
David Buck