Verificando se um objeto é um determinado tipo no Swift

267

Eu tenho uma matriz que é composta AnyObject. Eu quero iterar sobre ele e encontrar todos os elementos que são instâncias de matriz.

Como posso verificar se um objeto é de um determinado tipo no Swift?

Encore PTL
fonte
Sua pergunta é sobre como encontrar o tipo de um determinado objeto, mas você aceitou uma resposta que só é capaz de verificar se um objeto é de um determinado tipo. Sugiro que você edite sua pergunta especificamente para que, caso contrário, muitos leitores ficarão insatisfeitos com a resposta que você aceitou. (Todas as outras respostas são semelhantes, por isso, felizmente você não precisa se preocupar com o que os torna inválida por estreitando sua pergunta.)
Jeremy Banks
Editei esta pergunta para desambiguá-la de stackoverflow.com/q/24093433 , que estou votando para reabrir. São perguntas úteis e similares, mas as respostas são bem distintas, portanto, seria útil mantê-las separadas.
Jeremy Banks
1
possível duplicata de Como você descobre o tipo de um objeto (em Swift)?
Esqarrouth

Respostas:

304

Se você deseja verificar um tipo específico, pode fazer o seguinte:

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

Você pode usar "como!" e isso gerará um erro de tempo de execução se objnão for do tipo[String]

let stringArray = obj as! [String]

Você também pode verificar um elemento de cada vez:

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
fonte
Por que isso geraria apenas um erro de tempo de execução e não um erro de tempo de compilação quando o ?não está presente? Soa como ase ?quando combinado irá realizar verificação de tempo de execução. Quando seria apropriado usar assem ?? Desde já, obrigado.
Unheilig
@ Unheilig Você só deve usar assem o ?se não houver como o seu programa se recuperar do objeto que não é desse tipo, porque o programa será interrompido imediatamente se não for. O uso de ?na ifinstrução permite que o programa continue.
drewag
Obrigado pela resposta. Corrija-me se estiver errado: pensei que o uso do ?nesse caso executaria uma verificação de tipo "genérico", se sim, na cláusula if, se não, na cláusula else. Sem o ?else nunca seria inserido e, como você apontou, causa um erro de tempo de execução. Obrigado novamente.
Unheilig
@ Unheilig Me desculpe, não entendo o que você está dizendo / perguntando. Isso ?permite que a atribuição retorne, nilfazendo com que a instrução if retorne falsee, portanto, caia na instrução else. No entanto, penso que a explicação ajuda com a compreensão, mas if leté na verdade um caso especial no compilador
drewag
1
@Unheilig correto, você pode usar var se você gostaria de ser capaz de modificar o valor, enquanto nesse âmbito local (essas alterações não afetarão fora do escopo)
drewag
202

No Swift 2.2 - 5, agora você pode:

if object is String
{
}

Em seguida, para filtrar sua matriz:

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

Se você tiver vários tipos para verificar:

    switch object
    {
    case is String:
        ...

    case is OtherClass:
        ...

    default:
        ...
    }
assuntos de significado
fonte
Essa solução é mais curta, mas tem uma desvantagem: você não pode usá-lo objectcomo um Stringsuporte interno (pelo menos no Swift 2), enquanto que com a letsolução, você pode fazê-lo.
Ferran Maylinch 18/01/19
@FerranMaylinch Não entendo o que você quer dizer com usar objecto bloco.
significado-importa
@ meaning-matter, por exemplo, você não poderá fazer isso object.uppercaseStringporque o tipo da variável não é convertido para esse tipo, você apenas verificou se o objeto (apontado pela variável) é umString
Ferran Maylinch
Como você pode fazer isso se o tipo de classe que você está verificando é arbitrário? Se você tiver apenas uma variável, precisará obter um tipo de classe?
Alex Zavatone 24/05/19
152

Se você deseja apenas saber se um objeto é um subtipo de um determinado tipo, existe uma abordagem mais simples:

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

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

“Use o operador de verificação de tipo (is) para verificar se uma instância é de um determinado tipo de subclasse. O operador de verificação de tipo retorna true se a instância for desse tipo de subclasse e false se não for. ” Trecho de: Apple Inc. “A linguagem de programação Swift”. iBooks .

No texto acima, a frase 'de um determinado tipo de subclasse' é importante. O uso de is Circlee is Rectangleé aceito pelo compilador porque esse valor shapeé declarado como Shape(uma superclasse de Circlee Rectangle).

Se você estiver usando tipos primitivos, a superclasse seria Any. Aqui está um exemplo:

 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
fonte
2
E se eu armazenasse um tipo primitivo em uma matriz ou se a matriz fosse do tipo primitivo, isainda funcionaria aqui? Obrigado.
Unheilig
Deverá funcionar se você declarar o objectas Any. Atualizado com um exemplo.
GoZoner
Obrigado pela resposta. Parece promissor. Minha única dúvida é que, de acordo com a resposta abaixo, na qual AnyObjecté sugerida, parece ter sido replicada por AnyObjectnão herdar NSObject. Se Anyfor diferente, isso também seria uma ótima solução. Obrigado.
Unheilig
21

Eu tenho 2 maneiras de fazer isso:

if let thisShape = aShape as? Square 

Ou:

aShape.isKindOfClass(Square)

Aqui está um exemplo detalhado:

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

Editar: 3 agora:

let myShape = Shape()
if myShape is Shape {
    print("yes it is")
}
Esqarrouth
fonte
1
isKindOfClassé um método do NSObjectprotocolo; ele só deve trabalhar para as classes que adotam (todas as classes descendentes de NSObject, além de todo o costume classe Swift, que adota-lo explicitamente)
Nicolas Miari
16

para swift4:

if obj is MyClass{
    // then object type is MyClass Type
}
Ahmad Labeeb
fonte
melhor resposta para mim
ruselli 30/04
9

Suponha que drawTriangle é uma instância do UIView. Para verificar se drawTriangle é do tipo UITableView:

No Swift 3 ,

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

Isso também pode ser usado para classes definidas por você. Você pode usar isso para verificar as subvisões de uma visualização.

emmmphd
fonte
5

Por que não usar a funcionalidade incorporada criada especialmente para esta tarefa?

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

Result: "Array<Any>"
Patrik Forsberg
fonte
A função type () é simples
:)
5

Esteja avisado sobre isso:

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) 

Todas as quatro últimas linhas retornam verdadeiras, porque se você digitar

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

... ele imprime "false", é claro, mas um aviso diz que a conversão de CGRect para String falha. Portanto, algum tipo é ponte, e a palavra-chave 'is' chama uma conversão implícita.

Você deve usar melhor um destes:

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

Se você quiser apenas verificar a classe sem receber um aviso por causa do valor definido não utilizado (deixe someVariable ...), basta substituir o material let por um booleano:

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

O Xcode propôs isso quando eu usei o let e não usei o valor definido.

the_mariooo
fonte
2

Por que não usar algo assim

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
}

no Swift 3.

Dawy
fonte
2

Swift 4.2. No meu caso, usando a função isKind.

isKind (of :) Retorna um valor booleano que indica se o receptor é uma instância de uma determinada classe ou uma instância de qualquer classe que herda dessa classe.

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

Leia mais https://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418511-iskind

Tung Tran
fonte
1
Isso não é rápido. É Cacau e só funciona onde ele iria trabalhar para C. Objetivo
mate
1

myObject as? Stringretorna nilse myObjectnão for a String. Caso contrário, ele retornará a String?, para que você possa acessar a própria string myObject!ou convertê-la com myObject! as Stringsegurança.

cprcrack
fonte
1

Swift 3:

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

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

Apenas para completar, com base na resposta aceita e em algumas outras:

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

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

Mas você também pode ( compactMaptambém "mapeia" os valores que filternão o fazem):

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

E uma versão usando 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
    }
}

Mas, mantendo a questão, para verificar se é uma matriz (ou seja [String]):

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

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

Ou de maneira mais geral (veja esta outra resposta da pergunta ):

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
fonte
1

as?nem sempre fornecerá o resultado esperado porque asnão testa se um tipo de dados é de um tipo específico, mas apenas se um tipo de dados pode ser convertido ou representado como um tipo específico.

Considere este código por exemplo:

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

Todos os tipos de dados em conformidade com o Errorprotocolo podem ser convertidos em um NSErrorobjeto, portanto, sempre será bem-sucedido . No entanto, isso não significa que errorseja de fato umNSError objeto ou uma subclasse dele.

Uma verificação de tipo correta seria:

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

No entanto, isso verifica apenas o tipo exato. Se você deseja incluir também a subclasse de NSError, você deve usar:

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

Se você tiver uma resposta como esta:

{
  "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 você deseja verificar o valor is_stuckedque será lido como AnyObject, tudo que você precisa fazer é isso

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

Se você não souber que receberá uma matriz de dicionários ou dicionário único na resposta do servidor, precisará verificar se o resultado contém uma matriz ou não.
No meu caso, sempre recebendo uma matriz de dicionários, exceto uma vez. Então, para lidar com isso, usei o código abaixo para o swift 3.

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

Aqui como? Matriz verifica se o valor obtido é matriz (de itens do dicionário). Caso contrário, você pode manipular se for um item de dicionário único que não é mantido dentro de uma matriz.

VS
fonte
0

Swift 5.2 e versão Xcode: 11.3.1 (11C504)

Aqui está minha solução de verificar o tipo de dados:

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

Espero que ajude você.

Spencer Reid
fonte
Ao responder a uma pergunta antiga, sua resposta seria muito mais útil para outros usuários do StackOverflow se você incluísse algum contexto para explicar como sua resposta ajuda, principalmente para uma pergunta que já tenha uma resposta aceita. Veja: Como escrevo uma boa resposta .
David Buck