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?
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?
Risposte:
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
}
}
?non è presente. Sembra che ase ?quando combinato eseguirà il controllo di runtime. Quando sarebbe appropriato usare assenza ?? Grazie in anticipo.
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.
?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.
?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
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:
...
}
objectcome Stringall'interno delle parentesi graffe (almeno in Swift 2), mentre con la letsoluzione è possibile farlo.
objectnel blocco va bene.
object.uppercaseStringperché il tipo della variabile non viene trasmesso a quel tipo, hai appena verificato che l'oggetto (puntato dalla variabile) sia unString
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"
isfunzionerebbe ancora qui? Grazie.
objectas Any. Aggiornato con un esempio.
AnyObjectviene suggerito, sembra essere stato replicato a causa della AnyObjectnon ereditarietà NSObject. Se Anyè diverso, allora in realtà sarebbe anche un'ottima soluzione. Grazie.
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")
}
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)
per swift4:
if obj is MyClass{
// then object type is MyClass Type
}
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.
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>"
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))
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.
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.
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
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")
}
}
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 {
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")
}
}
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.
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.