Vérifier si un objet est d'un type donné dans Swift

267

J'ai un tableau composé de AnyObject. Je veux le parcourir et trouver tous les éléments qui sont des instances de tableau.

Comment puis-je vérifier si un objet est d'un type donné dans Swift?

Encore PTL
la source
Votre question porte sur la recherche du type d'un objet donné, mais vous avez accepté une réponse qui est uniquement capable de vérifier si un objet est d'un type donné. Je vous suggère de modifier votre question spécifiquement pour cela, sinon de nombreux lecteurs seront insatisfaits de la réponse que vous avez acceptée. (Toutes les autres réponses sont similaires, donc heureusement, vous n'avez pas à vous soucier de les rendre invalides en restreignant votre question.)
Jeremy Banks
J'ai modifié cette question pour la lever de toute ambiguïté à partir de stackoverflow.com/q/24093433 , que je vote pour rouvrir. Ce sont des questions utiles et similaires, mais les réponses sont assez distinctes, il serait donc utile de les séparer.
Jeremy Banks

Réponses:

304

Si vous souhaitez vérifier par rapport à un type spécifique, vous pouvez procéder comme suit:

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

Vous pouvez utiliser "as!" et cela générera une erreur d'exécution s'il objn'est pas de type[String]

let stringArray = obj as! [String]

Vous pouvez également vérifier un élément à la fois:

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
la source
Pourquoi cela ne déclencherait-il qu'une erreur d'exécution et non une erreur de temps de compilation lorsque le ?n'est pas présent. On dirait aset ?lorsqu'il est combiné sera effectuer une vérification de l' exécution. Quand serait-il approprié de l'utiliser assans ?? Merci d'avance.
Unheilig
@Unheilig Vous ne devez utiliser assans le ?s'il n'y a aucun moyen que votre programme puisse récupérer de l'objet n'étant pas de ce type car le programme s'arrêtera immédiatement s'il ne l'est pas. L'utilisation de ?dans l' ifinstruction permet au programme de continuer.
drewag
Merci pour la réponse. Corrigez-moi si je me trompe: je pensais que l'utilisation de ?dans ce cas effectuerait une vérification de type "générique", si oui, à la clause if, sinon, à la clause else. Sans le ?reste, il ne serait jamais entré et, comme vous l'avez souligné, provoquait une erreur d'exécution. Merci encore.
Unheilig
@Unheilig Je suis désolé, je ne comprends pas ce que vous dites / demandez. Le ?permet à l'affectation de retourner, nilprovoquant le retour de l'instruction if falseet donc de passer à l'instruction else. Cependant, je pense que cette explication aide à la compréhension, mais if letc'est en fait un cas spécial dans le compilateur
drewag
1
@Unheilig Correct, vous pouvez utiliser var si vous souhaitez pouvoir modifier la valeur dans cette portée locale (ces changements n'affecteront pas en dehors de la portée)
drewag
202

Dans Swift 2.2 - 5, vous pouvez désormais:

if object is String
{
}

Puis pour filtrer votre tableau:

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

Si vous avez plusieurs types à vérifier:

    switch object
    {
    case is String:
        ...

    case is OtherClass:
        ...

    default:
        ...
    }
le sens compte
la source
Cette solution est plus courte, mais elle a un inconvénient: vous ne pouvez pas utiliser le objectcomme un Stringà l'intérieur des accolades (au moins dans Swift 2), tandis qu'avec la letsolution, vous pouvez le faire.
Ferran Maylinch
@FerranMaylinch Je ne comprends pas ce que vous voulez dire car utiliser objectdans le bloc est très bien.
signification est importante du
@ sens-importe, par exemple, vous ne pourrez pas le faire object.uppercaseStringcar le type de la variable n'est pas converti en ce type, vous venez de vérifier que l'objet (pointé par la variable) est unString
Ferran Maylinch
Comment pouvez-vous faire cela si le type de classe que vous recherchez est arbitraire? Si vous n'avez qu'une variable dont vous avez besoin pour obtenir un type de classe?
Alex Zavatone
152

Si vous voulez seulement savoir si un objet est un sous-type d'un type donné, il existe une approche plus simple:

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

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

«Utilisez l'opérateur de vérification de type (is) pour vérifier si une instance est d'un certain type de sous-classe. L'opérateur de vérification de type renvoie true si l'instance appartient à ce type de sous-classe et false dans le cas contraire. » Extrait de: Apple Inc. «The Swift Programming Language». iBooks .

Dans ce qui précède, l'expression «d'un certain type de sous-classe» est importante. L'utilisation de is Circleet is Rectangleest acceptée par le compilateur car cette valeur shapeest déclarée comme Shape(une superclasse de Circleet Rectangle).

Si vous utilisez des types primitifs, la superclasse serait Any . Voici un exemple:

 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
la source
2
Que se passe-t-il si j'ai stocké un type primitif dans un tableau ou si le tableau est du type primitif, fonctionnerait istoujours ici? Merci.
Unheilig
Cela devrait fonctionner si vous déclarez le objectas Any. Mis à jour avec un exemple.
GoZoner
Merci pour votre réponse. Cela semble prometteur. Mon seul doute est que, selon la réponse ci-dessous, dans laquelle AnyObjectest suggéré, semble avoir été répliqué en raison de AnyObjectne pas hériter de NSObject. Si Anyc'est différent, ce serait en fait une excellente solution également. Merci.
Unheilig
21

J'ai 2 façons de le faire:

if let thisShape = aShape as? Square 

Ou:

aShape.isKindOfClass(Square)

Voici un exemple détaillé:

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

Modifier: 3 maintenant:

let myShape = Shape()
if myShape is Shape {
    print("yes it is")
}
Esqarrouth
la source
1
isKindOfClassest une méthode du NSObjectprotocole; il ne devrait fonctionner que pour les classes qui l'adoptent (toutes les classes descendant de NSObject, plus toute classe Swift personnalisée qui l'adopte explicitement)
Nicolas Miari
16

pour swift4:

if obj is MyClass{
    // then object type is MyClass Type
}
Ahmad Labeeb
la source
meilleure réponse pour moi
ruselli
9

Supposons drawTriangle est une instance de UIView. Pour vérifier si drawTriangle est de type UITableView:

Dans Swift 3 ,

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

Cela pourrait également être utilisé pour les classes définies par vous-même. Vous pouvez l'utiliser pour vérifier les sous-vues d'une vue.

emmmphd
la source
5

Pourquoi ne pas utiliser la fonctionnalité intégrée spécialement conçue pour cette tâche?

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

Result: "Array<Any>"
Patrik Forsberg
la source
la fonction type () est tout simplement simple
:)
5

Soyez averti à ce sujet:

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) 

Les quatre dernières lignes renvoient true, c'est parce que si vous tapez

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

... il affiche "faux" bien sûr, mais un avertissement indique que la conversion de CGRect en chaîne échoue. Certains types sont donc pontés, et le mot-clé 'is' appelle un transtypage implicite.

Vous devriez mieux utiliser l'un d'eux:

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

Si vous voulez simplement vérifier la classe sans recevoir d'avertissement à cause de la valeur définie inutilisée (let someVariable ...), vous pouvez simplement remplacer le let stuff par un booléen:

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

Xcode l'a proposé lorsque j'ai utilisé la méthode let et que je n'ai pas utilisé la valeur définie.

the_mariooo
la source
2

Pourquoi ne pas utiliser quelque chose comme ça

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
}

dans Swift 3.

Dawy
la source
2

Swift 4.2, dans mon cas, en utilisant la fonction isKind.

isKind (of :) Renvoie une valeur booléenne qui indique si le récepteur est une instance de la classe donnée ou une instance de toute classe qui hérite de cette classe.

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

En savoir plus https://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418511-iskind

Tung Tran
la source
1
Ce n'est pas Swift. Il s'agit de Cocoa et ne fonctionne que là où cela fonctionnerait pour l'objectif C.
Matt
1

myObject as? Stringrenvoie nilsi myObjectn'est pas un String. Sinon, il renvoie un String?, de sorte que vous pouvez accéder à la chaîne elle-même avec myObject!ou la convertir en myObject! as Stringtoute sécurité.

cprcrack
la source
1

Swift 3:

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

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

Juste pour être complet, basé sur la réponse acceptée et quelques autres:

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

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

Mais vous pouvez aussi ( compactMapaussi "mapper" les valeurs qui filterne le font pas):

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

Et une version utilisant 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
    }
}

Mais en restant sur la question, pour vérifier s'il s'agit d'un tableau (ie [String]):

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

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

Ou plus généralement (voir cette autre question réponse ):

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
la source
1

as?ne vous donnera pas toujours le résultat attendu, car asil ne teste pas si un type de données est d'un type spécifique, mais uniquement si un type de données peut être converti ou représenté comme un type spécifique.

Considérez ce code par exemple:

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

Chaque type de données conforme au Errorprotocole peut être converti en NSErrorobjet, cela réussira donc toujours . Pourtant, cela ne signifie pas qu'il errors'agit en fait d'un NSErrorobjet ou d'une sous-classe de celui-ci.

Une vérification de type correcte serait:

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

Cependant, cela vérifie uniquement le type exact. Si vous souhaitez également inclure la sous-classe de NSError, vous devez utiliser:

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

Si vous avez une réponse comme celle-ci:

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

et vous voulez vérifier la valeur is_stuckedqui sera lue comme AnyObject, tout ce que vous avez à faire est ceci

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

Si vous ne savez pas que vous obtiendrez un tableau de dictionnaires ou un dictionnaire unique dans la réponse du serveur, vous devez vérifier si le résultat contient un tableau ou non.
Dans mon cas, je reçois toujours un éventail de dictionnaires, sauf une fois. Donc, pour gérer cela, j'ai utilisé le code ci-dessous pour swift 3.

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

Ici comme? Array vérifie si la valeur obtenue est un tableau (d'éléments de dictionnaire). Dans les autres cas, vous pouvez gérer s'il s'agit d'un élément de dictionnaire unique qui n'est pas conservé dans un tableau.

CONTRE
la source
0

Version Swift 5.2 et Xcode: 11.3.1 (11C504)

Voici ma solution de vérification du type de données:

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

J'espère que cela vous aidera.

Spencer Reid
la source
Lorsque vous répondez à une ancienne question, votre réponse serait beaucoup plus utile aux autres utilisateurs de StackOverflow si vous incluez un contexte pour expliquer en quoi votre réponse est utile, en particulier pour une question qui a déjà une réponse acceptée. Voir: Comment écrire une bonne réponse .
David Buck