Überprüfen, ob ein Objekt in Swift ein bestimmter Typ ist


267

Ich habe ein Array, das besteht AnyObject. Ich möchte darüber iterieren und alle Elemente finden, die Array-Instanzen sind.

Wie kann ich in Swift überprüfen, ob ein Objekt von einem bestimmten Typ ist?


In Ihrer Frage wird nach dem Typ eines bestimmten Objekts gefragt, aber Sie haben eine Antwort akzeptiert, mit der nur überprüft werden kann, ob ein Objekt von einem bestimmten Typ ist. Ich schlage vor, dass Sie Ihre Frage speziell so bearbeiten, dass sonst viele Leser mit der Antwort, die Sie akzeptiert haben, unzufrieden sind. (Alle anderen Antworten sind ähnlich, so dass Sie sich zum Glück keine Sorgen machen müssen, sie ungültig zu machen, indem Sie Ihre Frage eingrenzen.)
Jeremy Banks

Ich habe diese Frage bearbeitet, um sie von stackoverflow.com/q/24093433 zu unterscheiden , für dessen Wiedereröffnung ich stimme . Sie sind beide nützliche, ähnliche Fragen, aber die Antworten sind sehr unterschiedlich, so dass es nützlich wäre, sie getrennt zu halten.
Jeremy Banks

Antworten:


304

Wenn Sie mit einem bestimmten Typ vergleichen möchten, können Sie Folgendes tun:

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

Sie können "as!" und das wird einen Laufzeitfehler auslösen, wenn objnicht vom Typ ist[String]

let stringArray = obj as! [String]

Sie können auch jeweils ein Element überprüfen:

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

Warum würde das nur einen Laufzeitfehler und keinen Kompilierungsfehler auslösen, wenn der ?nicht vorhanden ist? Es klingt wie asund ?wenn sie kombiniert werden Laufzeitüberprüfung durchführen. Wann wäre es angebracht, asohne zu verwenden ?? Danke im Voraus.
Unheilig

@Unheilig Sie sollten nur asohne verwenden, ?wenn Ihr Programm auf keinen Fall von dem Objekt wiederhergestellt werden kann, das nicht von diesem Typ ist, da das Programm sofort angehalten wird, wenn dies nicht der Fall ist. Mit der ?in der ifAnweisung kann das Programm fortgesetzt werden.
Drawag

Danke für die Antwort. Korrigieren Sie mich, wenn ich falsch liege: Ich dachte, dass die Verwendung von ?in diesem Fall eine "generische" Typprüfung durchführen würde, wenn ja, zur if-Klausel, wenn nicht, zur else-Klausel. Ohne das ?würde sonst nie eingegeben werden und wie Sie betonten, einen Laufzeitfehler verursachen. Danke noch einmal.
Unheilig

@Unheilig Es tut mir leid, ich verstehe nicht, was du sagst / fragst. Dies ?ermöglicht die Rückgabe der Zuweisung, nilwodurch die if-Anweisung zurückgegeben wird falseund somit zur else-Anweisung durchfällt. Ich denke jedoch, dass die Erklärung beim Verständnis hilft, aber if lettatsächlich ein Sonderfall im Compiler ist
Drawag

1
@Unheilig Richtig, Sie können var verwenden, wenn Sie den Wert in diesem lokalen Bereich ändern möchten (diese Änderungen wirken sich nicht außerhalb des Bereichs aus)
drawag

202

In Swift 2.2 - 5 können Sie jetzt Folgendes tun:

if object is String
{
}

Dann filtern Sie Ihr Array:

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

Wenn Sie mehrere Typen überprüfen müssen:

    switch object
    {
    case is String:
        ...

    case is OtherClass:
        ...

    default:
        ...
    }

Diese Lösung ist kürzer, hat aber einen Nachteil: Sie können sie nicht objectals StringInnenseite der Klammern verwenden (zumindest in Swift 2), während Sie dies mit der letLösung tun können.
Ferran Maylinch

@FerranMaylinch Verstehe nicht, was du meinst, weil die Verwendung objectim Block in Ordnung ist.
Sinn-Angelegenheiten

@ Bedeutung-Angelegenheiten zB können Sie nicht tun, object.uppercaseStringweil der Typ der Variablen nicht in diesen Typ umgewandelt wird. Sie haben nur überprüft, dass das Objekt (auf das die Variable zeigt) ein istString
Ferran Maylinch

Wie können Sie dies tun, wenn Ihr Klassentyp, nach dem Sie suchen, willkürlich ist? Wenn Sie nur eine Variable haben, müssen Sie einen Klassentyp abrufen?
Alex Zavatone

152

Wenn Sie nur wissen , ob ein Objekt ist ein Subtyp eines bestimmten Typs dann gibt es einen einfacheren Ansatz ist:

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

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

„Verwenden Sie den Typprüfungsoperator (is), um zu überprüfen, ob eine Instanz von einem bestimmten Unterklassentyp ist. Der Typprüfungsoperator gibt true zurück, wenn die Instanz von diesem Unterklassentyp ist, und false, wenn dies nicht der Fall ist. “ Auszug aus: Apple Inc. "Die schnelle Programmiersprache". iBooks .

Oben ist der Ausdruck "eines bestimmten Unterklassentyps" wichtig. Die Verwendung von is Circleund is Rectanglewird vom Compiler akzeptiert, da dieser Wert shapeals Shape(eine Oberklasse von Circleund Rectangle) deklariert ist .

Wenn Sie primitive Typen verwenden, wäre die Oberklasse Any. Hier ist ein Beispiel:

 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
Was würde hier isnoch funktionieren, wenn ich einen primitiven Typ in einem Array gespeichert hätte oder wenn das Array vom primitiven Typ ist ? Vielen Dank.
Unheilig

Es sollte funktionieren, wenn Sie das objectals deklarieren Any. Mit einem Beispiel aktualisiert.
GoZoner

Danke für die Antwort. Es sieht vielversprechend aus. Mein einziger Zweifel ist, dass gemäß der unten stehenden Antwort, in der AnyObjectvorgeschlagen wird, eine Erwiderung erfolgt zu sein scheint, weil ich AnyObjectnicht von geerbt habe NSObject. Wenn dies Anyanders ist, wäre dies in der Tat auch eine großartige Lösung. Vielen Dank.
Unheilig

21

Ich habe zwei Möglichkeiten:

if let thisShape = aShape as? Square 

Oder:

aShape.isKindOfClass(Square)

Hier ist ein detailliertes Beispiel:

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

Edit: 3 jetzt:

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

1
isKindOfClassist eine Methode des NSObjectProtokolls; Es sollte nur für Klassen funktionieren, die es übernehmen (alle Klassen, die von NSObject abstammen, sowie alle benutzerdefinierten Swift-Klassen, die es explizit übernehmen)
Nicolas Miari


9

Angenommen, drawTriangle ist eine Instanz von UIView. So prüfen Sie, ob drawTriangle vom Typ UITableView ist:

In Swift 3 ,

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

Dies kann auch für von Ihnen definierte Klassen verwendet werden. Sie können dies verwenden, um Unteransichten einer Ansicht zu überprüfen.


5

Warum nicht die speziell für diese Aufgabe entwickelte Funktionalität nutzen?

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

Result: "Array<Any>"

Die Funktion type () ist einfach
:)

5

Seien Sie gewarnt:

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) 

Alle vier letzten Zeilen geben true zurück. Dies liegt daran, dass Sie eingeben

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

... es wird natürlich "false" ausgegeben, aber eine Warnung besagt, dass die Umwandlung von CGRect in String fehlschlägt. Einige Typen werden also überbrückt, und das Schlüsselwort 'is' ruft eine implizite Umwandlung auf.

Sie sollten besser eines davon verwenden:

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

2

Wenn Sie die Klasse aufgrund des nicht verwendeten definierten Werts (let someVariable ...) nur überprüfen möchten, ohne eine Warnung zu erhalten, können Sie das let-Zeug einfach durch einen Booleschen Wert ersetzen:

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

Xcode schlug dies vor, als ich den let-Weg verwendete und den definierten Wert nicht verwendete.


2

Warum nicht so etwas benutzen?

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, In meinem Fall mit der isKind-Funktion.

isKind (of :) Gibt einen booleschen Wert zurück, der angibt, ob der Empfänger eine Instanz einer bestimmten Klasse oder eine Instanz einer Klasse ist, die von dieser Klasse erbt.

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

Lesen Sie mehr https://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418511-iskind


1
Das ist nicht schnell. Es ist Kakao und funktioniert nur dort, wo es für Ziel C funktionieren würde.
Matt

1

myObject as? Stringgibt zurück, nilwenn myObjectnicht a String. Andernfalls wird a zurückgegeben String?, sodass Sie mit auf die Zeichenfolge selbst zugreifen myObject!oder sie myObject! as Stringsicher umwandeln können.


1

Swift 3:

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

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

1

Nur der Vollständigkeit halber basierend auf der akzeptierten Antwort und einigen anderen:

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

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

Sie können aber auch ( compactMapauch die Werte "abbilden", was filternicht der Fall ist):

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

Und eine Version mit 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
    }
}

Aber bleiben Sie bei der Frage, um zu überprüfen, ob es sich um ein Array handelt (dh [String]):

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

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

Oder allgemeiner (siehe diese andere Frage Antwort ):

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?gibt Ihnen nicht immer das erwartete Ergebnis , da asnicht testen , ob ein Datentyp ist von einer bestimmten Art , sondern nur dann , wenn ein Datentyp werden kann umgewandelt oder als dargestellt spezifische Art.

Betrachten Sie diesen Code zum Beispiel:

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

Jeder dem ErrorProtokoll entsprechende Datentyp kann in ein NSErrorObjekt konvertiert werden, sodass dies immer erfolgreich ist . Dies bedeutet jedoch nicht, dass errores sich tatsächlich um ein NSErrorObjekt oder eine Unterklasse davon handelt.

Eine korrekte Typprüfung wäre:

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

Dies prüft jedoch nur den genauen Typ. Wenn Sie auch eine Unterklasse von einschließen möchten NSError, sollten Sie Folgendes verwenden:

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

0

Wenn Sie eine Antwort wie diese haben:

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

und Sie möchten nach Werten is_stuckedsuchen, die als AnyObject gelesen werden. Alles, was Sie tun müssen, ist dies

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

0

Wenn Sie nicht wissen, dass Sie in der Antwort vom Server ein Array von Wörterbüchern oder ein einzelnes Wörterbuch erhalten, müssen Sie überprüfen, ob das Ergebnis ein Array enthält oder nicht.
In meinem Fall immer eine Reihe von Wörterbüchern erhalten, außer einmal. Um damit umzugehen, habe ich den folgenden Code für Swift 3 verwendet.

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

Hier als? Array prüft, ob der erhaltene Wert ein Array (von Wörterbuchelementen) ist. In anderen Fällen können Sie damit umgehen, ob es sich um ein einzelnes Wörterbuchelement handelt, das nicht in einem Array gespeichert ist.


0

Swift 5.2 & Xcode Version: 11.3.1 (11C504)

Hier ist meine Lösung zur Überprüfung des Datentyps:

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

Ich hoffe es hilft dir.


Wenn Sie eine alte Frage beantworten, ist Ihre Antwort für andere StackOverflow-Benutzer viel nützlicher, wenn Sie einen Kontext angeben, um zu erklären, wie Ihre Antwort hilft, insbesondere für eine Frage, für die bereits eine Antwort akzeptiert wurde. Siehe auch : Wie kann ich eine gute Antwort schreiben .
David Buck
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.