Verifica se un oggetto è un determinato tipo in Swift


267

Ho un array composto da AnyObject. Voglio iterare su di esso e trovare tutti gli elementi che sono istanze di array.

Come posso verificare se un oggetto è di un determinato tipo in Swift?


La tua domanda ti chiede di trovare il tipo di un determinato oggetto, ma hai accettato una risposta che è solo in grado di verificare se un oggetto è di un determinato tipo. Ti suggerisco di modificare la tua domanda in modo specifico, altrimenti molti lettori non saranno soddisfatti della risposta che hai accettato. (Tutte le altre risposte sono simili, quindi per fortuna non devi preoccuparti di renderle non valide restringendo la tua domanda.)
Jeremy Banks

Ho modificato questa domanda per chiarirla da stackoverflow.com/q/24093433 , che voterò per riaprire. Sono entrambe domande utili, simili, ma le risposte sono abbastanza distinte, quindi sarebbe utile tenerle separate.
Jeremy Banks,

Risposte:


304

Se si desidera verificare un tipo specifico, è possibile effettuare le seguenti operazioni:

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

Puoi usare "as!" e questo genererà un errore di runtime se objnon è di tipo[String]

let stringArray = obj as! [String]

Puoi anche controllare un elemento alla volta:

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

Perché ciò genererebbe solo un errore di runtime e non un errore di compilazione quando ?non è presente. Sembra che ase ?quando combinato eseguirà il controllo di runtime. Quando sarebbe appropriato usare assenza ?? Grazie in anticipo.
Unheilig,

@Unheilig si dovrebbe usare assenza la ?se non v'è alcun modo il vostro programma potrebbe recuperare dall'oggetto non essere di quel tipo perché il programma immediatamente fermare se non lo è. L'uso di ?in ifnell'istruzione consente al programma di continuare.
Drewag,

Grazie per la risposta Correggimi se sbaglio: ho pensato che l'utilizzo di ?in questo caso avrebbe eseguito un controllo di tipo "generico", in caso affermativo, alla clausola if, in caso contrario, alla clausola else. Senza l' ?altro non verrebbe mai inserito e come hai sottolineato causerebbe un errore di runtime. Grazie ancora.
Unheilig,

@Unheilig Mi dispiace, non capisco cosa stai dicendo / chiedendo. Ciò ?consente di restituire l'assegnazione nilcausando la restituzione dell'istruzione if falsee quindi la restituzione dell'istruzione else. Tuttavia, penso che questa spiegazione sia di aiuto nella comprensione, ma in if letrealtà è un caso speciale nel compilatore
drewag

1
@Unheilig Corretto, è possibile utilizzare var se si desidera poter modificare il valore all'interno di tale ambito locale (tali modifiche non influiranno al di fuori dell'ambito)
drewag

202

In Swift 2.2 - 5 ora puoi fare:

if object is String
{
}

Quindi per filtrare l'array:

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

Se hai più tipi da controllare:

    switch object
    {
    case is String:
        ...

    case is OtherClass:
        ...

    default:
        ...
    }

Questa soluzione è più breve, ma ha uno svantaggio: non è possibile utilizzare il objectcome Stringall'interno delle parentesi graffe (almeno in Swift 2), mentre con la letsoluzione è possibile farlo.
Ferran Maylinch,

@FerranMaylinch Non capisco cosa intendi perché usare objectnel blocco va bene.
significato-importa

@ significato-argomenti, ad es. non sarai in grado di farlo object.uppercaseStringperché il tipo della variabile non viene trasmesso a quel tipo, hai appena verificato che l'oggetto (puntato dalla variabile) sia unString
Ferran Maylinch

Come puoi farlo se il tuo tipo di classe che stai controllando è arbitrario? Se hai solo una variabile di cui hai bisogno per ottenere un tipo di classe?
Alex Zavatone,

152

Se vuoi solo sapere se un oggetto è un sottotipo di un determinato tipo, esiste un approccio più semplice:

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

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

"Utilizzare l'operatore di verifica del tipo (is) per verificare se un'istanza è di un determinato tipo di sottoclasse. L'operatore di verifica del tipo restituisce true se l'istanza è di quel tipo di sottoclasse e false se non lo è. " Estratto da: Apple Inc. "The Swift Programming Language".iBooks .

In quanto sopra è importante la frase "di un certo tipo di sottoclasse". L'uso di is Circlee is Rectangleè accettato dal compilatore perché quel valore shapeè dichiarato come Shape(una superclasse di CircleeRectangle ).

Se stai usando tipi primitivi, la superclasse sarebbe Any. Ecco un esempio:

 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"

2
E se memorizzassi un tipo primitivo in un array o se l'array è di tipo primitivo, isfunzionerebbe ancora qui? Grazie.
Unheilig,

Dovrebbe funzionare se si dichiara objectas Any. Aggiornato con un esempio.
GoZoner,

Grazie per la risposta. Sembra promettente. Il mio unico dubbio è che, secondo la risposta che segue, in cui AnyObjectviene suggerito, sembra essere stato replicato a causa della AnyObjectnon ereditarietà NSObject. Se Anyè diverso, allora in realtà sarebbe anche un'ottima soluzione. Grazie.
Unheilig,

21

Ho 2 modi per 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")
}

Modifica: 3 ora:

let myShape = Shape()
if myShape is Shape {
    print("yes it is")
}

1
isKindOfClassè un metodo del NSObjectprotocollo; dovrebbe funzionare solo per le classi che lo adottano (tutte le classi discendenti da NSObject, oltre a qualsiasi classe Swift personalizzata che lo adotti esplicitamente)
Nicolas Miari,


9

Assumi drawTriangle sia un'istanza di UIView. Per verificare se drawTriangle è di tipo UITableView:

In Swift 3 ,

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

Questo potrebbe anche essere usato per le classi definite da te. Puoi usarlo per controllare le viste secondarie di una vista.


5

Perché non utilizzare la funzionalità integrata creata appositamente per questa attività?

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

Result: "Array<Any>"

La funzione type () è semplicemente semplice
:)

5

Attenzione:

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) 

Tutte e quattro le ultime righe restituiscono true, questo perché se digiti

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

... stampa "falso" ovviamente, ma un avvertimento dice che il Cast da CGRect a String fallisce. Quindi alcuni tipi sono collegati a ponte, e la parola chiave "is" chiama un cast implicito.

Dovresti usare uno di questi:

myObject.isKind(of: MyClass.self)) 
myObject.isMember(of: MyClass.self))

2

Se vuoi solo controllare la classe senza ricevere un avviso a causa del valore definito inutilizzato (let someVariable ...), puoi semplicemente sostituire le cose let con un valore booleano:

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

Xcode lo ha proposto quando ho usato il modo let e non ho usato il valore definito.


2

Perché non usare qualcosa del genere

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
}

in Swift 3.


2

Swift 4.2, Nel mio caso, usando la funzione isKind.

isKind (of :) Restituisce un valore booleano che indica se il destinatario è un'istanza di una determinata classe o un'istanza di qualsiasi classe che eredita da quella classe.

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

Leggi di più https://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418511-iskind


1
Questo non è Swift. È Cocoa e funziona solo dove avrebbe funzionato per l'Obiettivo C.
matt

1

myObject as? Stringritorna nilse myObjectnon è un String. Altrimenti, restituisce a String?, in modo da poter accedere alla stringa stessa myObject!o lanciarla con myObject! as Stringsicurezza.


1

Swift 3:

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

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

1

Solo per completezza basata sulla risposta accettata e su alcuni altri:

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

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

Ma puoi anche ( compactMapanche "mappare" i valori che filternon lo fanno):

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

E una versione che utilizza 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
    }
}

Ma attenendosi alla domanda, per verificare se si tratta di un array (cioè [String]):

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

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

O più in generale (vedi questa altra risposta alla domanda ):

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

1

as?non ti darà sempre il risultato atteso perché asnon verifica se un tipo di dati è di un tipo specifico ma solo se un tipo di dati può essere convertito o rappresentato come tipo specifico.

Considera questo codice per esempio:

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

Ogni tipo di dati conforme al Errorprotocollo può essere convertito in un NSErroroggetto, quindi ciò avrà sempre successo . Tuttavia ciò non significa che errorin realtà sia un NSErroroggetto o una sua sottoclasse.

Un controllo del tipo corretto sarebbe:

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

Tuttavia, questo controlla solo il tipo esatto. Se si desidera includere anche la sottoclasse di NSError, è necessario utilizzare:

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

0

Se hai una risposta come questa:

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

e vuoi verificare il valore is_stuckedche verrà letto come AnyObject, tutto ciò che devi fare è questo

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

0

Se non sai che otterrai una matrice di dizionari o un singolo dizionario nella risposta dal server devi controllare se il risultato contiene una matrice o meno.
Nel mio caso ricevo sempre una serie di dizionari tranne una volta. Quindi, per gestirlo ho usato il codice seguente per swift 3.

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

Qui come? Array controlla se il valore ottenuto è array (di voci del dizionario). In caso contrario, è possibile gestire se si tratta di un singolo elemento del dizionario che non viene conservato in un array.


0

Swift 5.2 e versione Xcode: 11.3.1 (11C504)

Ecco la mia soluzione di controllo del tipo di dati:

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

Spero che ti possa aiutare.


Quando rispondi a una vecchia domanda, la tua risposta sarebbe molto più utile per gli altri utenti StackOverflow se includessi un contesto per spiegare come la tua risposta aiuta, in particolare per una domanda che ha già una risposta accettata. Vedi: Come scrivo una buona risposta .
David Buck, il
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.