Sprawdzanie, czy obiekt jest danego typu w Swift

267

Mam tablicę, która składa się z AnyObject. Chcę iterować i znaleźć wszystkie elementy, które są instancjami tablicowymi.

Jak mogę sprawdzić, czy obiekt jest danego typu w Swift?

Bis PTL
źródło
Twoje pytanie dotyczy znalezienia typu danego obiektu, ale zaakceptowałeś odpowiedź, która może tylko sprawdzić, czy obiekt jest danego typu. Sugeruję, abyś zmodyfikował swoje pytanie w taki sposób, aby w przeciwnym razie wielu czytelników nie byłoby zadowolonych z odpowiedzi, którą zaakceptowałeś. (Wszystkie pozostałe odpowiedzi są podobne, więc na szczęście nie musisz się martwić, że unieważnisz je, zawężając swoje pytanie.)
Jeremy Banks,
Zredagowałem to pytanie, aby ujednoznacznić je ze stackoverflow.com/q/24093433 , które głosuję, aby je ponownie otworzyć. Oba są użyteczne, podobne, pytania, ale odpowiedzi są dość różne, więc dobrze byłoby je rozdzielić.
Jeremy Banks
1
możliwy duplikat Jak znaleźć typ obiektu (w Swift)?
Esqarrouth,

Odpowiedzi:

304

Jeśli chcesz sprawdzić konkretny typ, możesz wykonać następujące czynności:

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

Możesz użyć „jak!” i spowoduje to błąd w czasie wykonywania, jeśli objnie jest typu[String]

let stringArray = obj as! [String]

Możesz również sprawdzić jeden element na raz:

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
   }
}
Drawag
źródło
Dlaczego miałoby to wyrzucać błąd czasu wykonywania, a nie błąd czasu kompilacji, gdy ?nie jest obecny. To brzmi jak asi ?po połączeniu wykona kontrolę czasu wykonywania. Kiedy byłoby właściwe używać asbez ?? Z góry dziękuję.
Unheilig
@Unheilig Należy używać tylko asbez ?jeśli nie ma sposobu, Twój program może odzyskać przedmiot nie jest tego rodzaju, ponieważ program będzie natychmiast zatrzymać, jeśli tak nie jest. Użycie instrukcji ?in ifpozwala kontynuować program.
Drawag
Dzięki za odpowiedź. Popraw mnie, jeśli się mylę: Myślałem, że użycie ?w tym przypadku spowoduje sprawdzenie typu „ogólnego”, jeśli tak, do klauzuli if, jeśli nie, do klauzuli else. Bez tego ?nigdy nie zostałby wprowadzony i, jak wskazałeś, powoduje błąd w czasie wykonywania. Dzięki jeszcze raz.
Unheilig
@ Unheilig Przepraszam, nie rozumiem o czym mówisz / pytasz. ?Umożliwia przypisanie do powrotu nilpowodując if do zwrotu false, a więc spada aż do innego rachunku. Myślę jednak, że wyjaśnienie to pomaga w zrozumieniu, ale if lettak naprawdę jest szczególnym przypadkiem w kompilatorze
Drawag
1
@Unheilig Prawidłowo, możesz użyć var, jeśli chcesz mieć możliwość modyfikowania wartości w tym lokalnym zasięgu (zmiany te nie będą miały wpływu poza zakres)
Drawag
202

W Swift 2.2 - 5 możesz teraz:

if object is String
{
}

Następnie, aby przefiltrować tablicę:

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

Jeśli masz wiele typów do sprawdzenia:

    switch object
    {
    case is String:
        ...

    case is OtherClass:
        ...

    default:
        ...
    }
znaczenie ma znaczenie
źródło
To rozwiązanie jest krótsze, ale ma tę wadę: nie można użyć go objectjako Stringnawiasów klamrowych (przynajmniej w Swift 2), podczas gdy w letrozwiązaniu można to zrobić.
Ferran Maylinch
@FerranMaylinch Nie rozumiem, co masz na myśli, ponieważ używanie objectw bloku jest w porządku.
znaczenie-ma znaczenie
@ znaczenie ma znaczenie, np. nie będziesz w stanie tego zrobić, object.uppercaseStringponieważ typ zmiennej nie jest rzutowany na ten typ, po prostu sprawdziłeś, że obiekt (wskazany przez zmienną) toString
Ferran Maylinch
Jak to zrobić, jeśli typ klasy, którą sprawdzasz, jest arbitralny? Jeśli masz tylko zmienną, z której musisz uzyskać typ klasy?
Alex Zavatone
152

Jeśli chcesz tylko wiedzieć, czy obiekt jest podtypem danego typu, istnieje prostsze podejście:

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

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

„Użyj operatora sprawdzania typu (is), aby sprawdzić, czy instancja jest określonego typu podklasy. Operator sprawdzania typu zwraca true, jeśli instancja jest tego typu podklasy, a false, jeśli nie jest. Fragment: Apple Inc. „Swift Programming Language”. iBooks .

W powyższym znaczeniu ważne jest wyrażenie „określonego typu podklasy”. Kompilator akceptuje użycie is Circlei, is Rectangleponieważ ta wartość shapejest zadeklarowana jako Shape(nadklasa Circlei Rectangle).

Jeśli używasz typów pierwotnych, nadklasą byłoby Any. Oto przykład:

 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
źródło
2
Co się stanie, jeśli zapisałem typ pierwotny w tablicy lub jeśli tablica jest typem pierwotnym, isnadal by tu działał? Dzięki.
Unheilig
Powinno działać, jeśli zadeklarujesz objectjako Any. Zaktualizowano z przykładem.
GoZoner,
Dzięki za odpowiedź. Wygląda obiecująco. Moją jedyną wątpliwością jest to, że zgodnie z poniższą odpowiedzią, w której AnyObjectjest sugerowana, wydaje się, że została odparta z powodu AnyObjectbraku dziedziczenia NSObject. Jeśli Anyjest inaczej, byłoby to również świetne rozwiązanie. Dzięki.
Unheilig
21

Mam na to 2 sposoby:

if let thisShape = aShape as? Square 

Lub:

aShape.isKindOfClass(Square)

Oto szczegółowy przykład:

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

Edytuj: 3 teraz:

let myShape = Shape()
if myShape is Shape {
    print("yes it is")
}
Esqarrouth
źródło
1
isKindOfClassjest metodą NSObjectprotokołu; powinno działać tylko dla klas, które przyjmują ją (wszystkie klasy zstępującego z NSObject oraz wszelkie niestandardowe klasy Swift że Przyjmuje jawnie)
Nicolas Miari
16

dla swift4:

if obj is MyClass{
    // then object type is MyClass Type
}
Ahmad Labeeb
źródło
najlepsza odpowiedź dla mnie
ruselli
9

Załóżmy, że drawTriangle jest instancją UIView. Aby sprawdzić, czy drawTriangle jest typu UITableView:

W Swift 3 ,

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

Można to również wykorzystać do klas zdefiniowanych przez ciebie. Możesz użyć tego do sprawdzenia widoków podrzędnych widoku.

emmmphd
źródło
5

Dlaczego nie skorzystać z wbudowanej funkcjonalności stworzonej specjalnie do tego zadania?

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

Result: "Array<Any>"
Patrik Forsberg
źródło
Funkcja type () jest po prostu prosta
:)
5

Ostrzegam o tym:

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) 

Wszystkie cztery ostatnie wiersze zwracają wartość true, ponieważ po wpisaniu

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

... drukuje „fałsz” oczywiście, ale Ostrzeżenie mówi, że rzutowanie z CGRect na ciąg nie powiedzie się. Więc niektóre typy są zmostkowane, a słowo kluczowe „is” wywołuje niejawną rzutowanie.

Lepiej użyj jednego z tych:

myObject.isKind(of: MyClass.self)) 
myObject.isMember(of: MyClass.self))
tontonCD
źródło
2

Jeśli chcesz tylko sprawdzić klasę bez ostrzeżenia z powodu nieużywanej zdefiniowanej wartości (niech someVariable ...), możesz po prostu zastąpić let przez boolean:

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

Xcode zaproponował to, gdy użyłem let let i nie użyłem zdefiniowanej wartości.

the_mariooo
źródło
2

Dlaczego nie skorzystać z czegoś takiego?

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
}

w Swift 3.

Dawy
źródło
2

Swift 4.2, w moim przypadku przy użyciu funkcji isKind.

isKind (of :) Zwraca wartość logiczną, która wskazuje, czy odbiornik jest instancją danej klasy, czy instancją dowolnej klasy, która dziedziczy z tej klasy.

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

Czytaj więcej https://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418511-iskind

Tung Tran
źródło
1
To nie jest szybkie. To jest Kakao i działa tylko tam, gdzie działałoby dla Celu C.
mat.
1

myObject as? Stringzwraca, niljeśli myObjectnie jest String. W przeciwnym razie zwraca a String?, dzięki czemu można uzyskać dostęp do samego ciągu za pomocą myObject!lub myObject! as Stringbezpiecznie rzucić go .

cprcrack
źródło
1

Swift 3:

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

if aShape.isKind(of: Circle.self) {
}
Harris
źródło
1

Dla kompletności opartej na przyjętej odpowiedzi i kilku innych:

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

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

Ale możesz także ( compactMaprównież „mapować” wartości, które filternie):

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

I wersja wykorzystująca 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
    }
}

Ale pozostając przy pytaniu, aby sprawdzić, czy jest to tablica (tj. [String]):

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

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

Lub bardziej ogólnie (patrz ta inna odpowiedź na pytanie ):

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
źródło
1

as?nie zawsze da oczekiwany wynik, ponieważ asnie sprawdza, czy typ danych jest określonego rodzaju, ale tylko wtedy, gdy typ danych można przekonwertować na lub reprezentować jako określony rodzaj.

Rozważ ten kod na przykład:

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

Każdy typ danych zgodny z Errorprotokołem można przekonwertować na NSErrorobiekt, więc zawsze się to powiedzie . Nie oznacza to jednak, że errorjest to w rzeczywistości NSErrorprzedmiot lub jego podklasa.

Prawidłowa kontrola typu to:

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

Sprawdza to jednak tylko dokładny typ. Jeśli chcesz również dołączyć podklasę NSError, powinieneś użyć:

func handleError ( error: Error ) {
    if error is NSError.Type {
Mecki
źródło
0

Jeśli masz taką odpowiedź:

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

i chcesz sprawdzić wartość, is_stuckedktóra zostanie odczytana jako AnyObject, wszystko co musisz zrobić, to to

if let isStucked = response["is_stucked"] as? Bool{
  if isStucked{
      print("is Stucked")
  }
  else{
      print("Not Stucked")
 }
}
Abo3atef
źródło
0

Jeśli nie wiesz, że w odpowiedzi z serwera otrzymasz tablicę słowników lub pojedynczy słownik, musisz sprawdzić, czy wynik zawiera tablicę, czy nie.
W moim przypadku zawsze otrzymuję szereg słowników, z wyjątkiem jednego. Aby to obsłużyć, użyłem poniższego kodu do szybkiego 3.

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

Tutaj jak? Tablica sprawdza, czy uzyskana wartość jest tablicą (elementów słownika). W innym przypadku możesz sobie poradzić, jeśli jest to pojedynczy element słownika, który nie jest przechowywany w tablicy.

VS
źródło
0

Wersja Swift 5.2 i Xcode: 11.3.1 (11C504)

Oto moje rozwiązanie sprawdzania typu danych:

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

Mam nadzieję, że ci to pomoże.

Spencer Reid
źródło
Odpowiadając na stare pytanie, twoja odpowiedź byłaby znacznie bardziej przydatna dla innych użytkowników StackOverflow, gdybyś podał kontekst wyjaśniający, w jaki sposób twoja odpowiedź pomaga, szczególnie w przypadku pytania, na które odpowiedź została już zaakceptowana. Zobacz: Jak napisać dobrą odpowiedź .
David Buck