Como você descobre o tipo de um objeto (em Swift)?

255

Ao tentar entender um programa, ou em alguns casos extremos, é útil poder realmente descobrir que tipo de coisa é. Eu sei que o depurador pode mostrar algumas informações de tipo, e você geralmente pode confiar na inferência de tipo para não especificar o tipo nessas situações, mas ainda assim, eu realmente gostaria de ter algo como o Python.type()

dynamicType (veja esta pergunta )

Atualização: isso foi alterado em uma versão recente do Swift, obj.dynamicTypeagora fornece uma referência ao tipo e não à instância do tipo dinâmico.

Este parece ser o mais promissor, mas até agora não consegui descobrir o tipo real

class MyClass {
    var count = 0
}

let mc = MyClass()

# update: this now evaluates as true
mc.dynamicType === MyClass.self

Eu também tentei usar uma referência de classe para instanciar um novo objeto, que faz o trabalho, mas estranhamente me deu um erro dizendo que eu preciso adicionar umrequired inicializador:

trabalho:

class MyClass {
    var count = 0
    required init() {
    }
}

let myClass2 = MyClass.self
let mc2 = MyClass2()

Ainda é apenas um pequeno passo para descobrir o tipo de um objeto em particular.

edit : removi um número substancial de detalhes agora irrelevantes - veja o histórico de edições se estiver interessado :)

Jiaaro
fonte
1
possível duplicata de Como obter a classe de uma variável no Swift?
David Berry
1
Curiosamente, print(mc)ou dump(mc)imprimirá um resumo (do qual você pode obter toString(mc)ou reflect(mc).summary), que conterá o nome da classe em algum lugar. Mas não está claro como obter apenas o nome da classe.
precisa saber é o seguinte
@ David semelhante, mas nem todas as variáveis ​​são instâncias de classe. Também que as perguntas era realmente sobre como verificar se o tipo de jogos que o programador está procurando, ao passo que eu estou esperando para apenas descobrir o tipo atacado
Jiaaro

Respostas:

284

Versão Swift 3:

type(of: yourObject)
Jérémy Lapointe
fonte
8
Fato engraçado. Isso não funciona com opcionais implicitamente desembrulhados! ie var myVar: SomeType!. O compilador fornece o erro "Não é possível converter o valor do tipo 'SomeType! .Type' (também conhecido como 'ImplicitlyUnwrappedOptional <SomeType> .Type') para o tipo de argumento esperado 'AnyClass' (também conhecido como 'AnyObject.Type') O compilador sugere a adição as! AnyClassapós o tipo, mas depois programa trava com alguma "EXC_BAD_INSTRUCTION" e outros jiberrish que eu não posso decifrar.
LightningStryk
1
De fato, essa deve ser a resposta aceita agora que o Swift 3 existe. Obrigado Jeremy!
biomiker
1
Se você estiver procurando pelo nome do tipo específico, quando o tipo for de um tipo de protocolo, isso poderá não funcionar para você.
Chris Príncipe
2
Se você tiver um Stringque é passado como tipo Any, type(of:)será exibido Any, não String.
ScottyBlades
1
@ScottyBlades então qual será a solução. Você pode prover?
Mubin Mall 23/01/19
109

Swift 2.0:

A maneira correta de fazer esse tipo de introspecção de tipo seria com a estrutura Mirror ,

    let stringObject:String = "testing"
    let stringArrayObject:[String] = ["one", "two"]
    let viewObject = UIView()
    let anyObject:Any = "testing"

    let stringMirror = Mirror(reflecting: stringObject)
    let stringArrayMirror = Mirror(reflecting: stringArrayObject)
    let viewMirror = Mirror(reflecting: viewObject)
    let anyMirror = Mirror(reflecting: anyObject)

Em seguida, para acessar o próprio tipo a partir da Mirrorestrutura, você usaria a propriedade da seguinte subjectTypemaneira:

    // Prints "String"
    print(stringMirror.subjectType)

    // Prints "Array<String>"
    print(stringArrayMirror.subjectType)

    // Prints "UIView"
    print(viewMirror.subjectType)

    // Prints "String"
    print(anyMirror.subjectType)

Você pode usar algo como isto:

    if anyMirror.subjectType == String.self {
        print("anyObject is a string!")
    } else {
        print("anyObject is not a string!")
    }
Gudbergur
fonte
7
Isso é ótimo. Lembre-se de que, se o objeto que está sendo espelhado for do tipo opcional, a comparação com um tipo não opcional falhará. Stringe Optional(String)não são os mesmos.
Thomas Verbeek
1
Exatamente o que eu estava procurando, queria saber qual é o tipo do objeto #
Joseph
Existe um tipo de comparação nesse contexto que não falha ao comparar tipos opcionais e não opcionais?
Chris Prince
Era isso que eu estava procurando. Obrigado @Gudbergur.
Mubin Mall
60

O dynamicType.printClassNamecódigo é de um exemplo no livro Swift. Não há como saber diretamente o nome de uma classe personalizada, mas você pode verificar um tipo de instância usando a ispalavra - chave, como mostrado abaixo. Este exemplo também mostra como implementar uma função className personalizada, se você realmente deseja o nome da classe como uma sequência.

class Shape {
    class func className() -> String {
        return "Shape"
    }
}

class Square: Shape {
    override class func className() -> String {
        return "Square"
    }
}

class Circle: Shape {
    override class func className() -> String {
        return "Circle"
    }
}

func getShape() -> Shape {
    return Square() // hardcoded for example
}

let newShape: Shape = getShape()
newShape is Square // true
newShape is Circle // false
newShape.dynamicType.className() // "Square"
newShape.dynamicType.className() == Square.className() // true

Nota: as
subclasses de NSObjectjá implementam sua própria função className. Se você estiver trabalhando com cacau, basta usar essa propriedade.

class MyObj: NSObject {
    init() {
        super.init()
        println("My class is \(self.className)")
    }
}
MyObj()
Traço
fonte
2
Ei, não tenho certeza de quando isso mudou, mas como Alex Pretzlav apontou, o comportamento mudou.
Jiaaro # 14/14
1
Sim. A partir do Swift 3.0, subjectTypenão está mais disponível e dynamicTypecausa uma mensagem de descontinuação do compilador.
Raphael
41

A partir de Xcode 6.0.1 (pelo menos, não tenho certeza de quando o adicionaram), seu exemplo original agora funciona:

class MyClass {
    var count = 0
}

let mc = MyClass()
mc.dynamicType === MyClass.self // returns `true`

Atualizar:

Para responder à pergunta original, você pode realmente usar o tempo de execução Objective-C com objetos Swift simples com êxito.

Tente o seguinte:

import Foundation
class MyClass { }
class SubClass: MyClass { }

let mc = MyClass()
let m2 = SubClass()

// Both of these return .Some("__lldb_expr_35.SubClass"), which is the fully mangled class name from the playground
String.fromCString(class_getName(m2.dynamicType))
String.fromCString(object_getClassName(m2))
// Returns .Some("__lldb_expr_42.MyClass")
String.fromCString(object_getClassName(mc))
Alex Pretzlav
fonte
Parece que eles mudaram para fornecer o tipo em vez de uma instância.
Jiaaro # 14/14
@Jiaaro, eu atualizei a minha resposta com o que eu acho que você estava procurando em sua pergunta original
Alex Pretzlav
36

Se você simplesmente precisar verificar se a variável é do tipo X ou se está em conformidade com algum protocolo, poderá usar isou as?como a seguir:

var unknownTypeVariable =if unknownTypeVariable is <ClassName> {
    //the variable is of type <ClassName>
} else {
    //variable is not of type <ClassName>
}

Isso é equivalente isKindOfClassem Obj-C.

E isso é equivalente a conformsToProtocol, ouisMemberOfClass

var unknownTypeVariable =if let myClass = unknownTypeVariable as? <ClassName or ProtocolName> {
    //unknownTypeVarible is of type <ClassName or ProtocolName>
} else {
    //unknownTypeVariable is not of type <ClassName or ProtocolName>
}
Valerii Lider
fonte
A segunda parte da sua resposta está errada. A declaração 'if let' com as?conversão condicional também faz o mesmo isKindOfClass, mas também fornece o resultado da conversão, caso ela seja bem-sucedida.
111316 awolf
O equivalente a isMemberOfClassé a condição object.dynamicType == ClassName.self.
111316 awolf
18

Swift 3:

if unknownType is MyClass {
   //unknownType is of class type MyClass
}
Peter
fonte
Eu acho que isexiste antes de Swift 3 ...?
Nicolas Miari
10

Para Swift 3.0

String(describing: <Class-Name>.self)

Para Swift 2.0 - 2.3

String(<Class-Name>)
techloverr
fonte
1
O importante sobre esta resposta estar correta para mim é que a sequência resultante corresponde exatamente ao nome da classe - para que eu possa usar isso para obter um nome de entidade dos Dados Principais de uma subclasse NSManagedObject. Eu usei a versão Swift3.
Kendall Helmstetter Gelner,
8

Aqui estão duas maneiras que eu recomendo:

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")
}
Esqarrouth
fonte
2
print( aShape is Square ), isoperador é mais preferível.
DawnSong
Boa solução para eu obter o tipo de objetos.
Nihasmata 19/12/19
1

Depende do caso de uso. Mas vamos supor que você queira fazer algo útil com seus tipos "variáveis". A switchdeclaração Swift é muito poderosa e pode ajudá-lo a obter os resultados que você está procurando ...

    let dd2 = ["x" : 9, "y" : "home9"]
    let dds = dd2.filter {
        let eIndex = "x"
        let eValue:Any = 9
        var r = false

        switch eValue {
        case let testString as String:
            r = $1 == testString
        case let testUInt as UInt:
            r = $1 == testUInt
        case let testInt as Int:
            r = $1 == testInt
        default:
            r = false
        }

        return r && $0 == eIndex
    }

Nesse caso, tenha um dicionário simples que contenha pares chave / valor que possam ser UInt, Int ou String. No .filter()método do dicionário, preciso me certificar de testar corretamente os valores e testar apenas uma String quando for uma string, etc. A instrução switch torna isso simples e seguro! Ao atribuir 9 à variável do tipo Qualquer, ele faz a troca para Int ser executada. Tente alterá-lo para:

   let eValue:Any = "home9"

..e tente novamente. Desta vez, ele executa o as Stringcaso.

CPD
fonte
1

Se você receber um aviso "sempre verdadeiro / falhar", pode ser necessário transmitir para Qualquer antes de usar is

(foo as Any) is SomeClass
josef
fonte
0
//: Playground - noun: a place where people can play

import UIKit

class A {
    class func a() {
        print("yeah")
    }

    func getInnerValue() {
        self.dynamicType.a()
    }
}

class B: A {
    override class func a() {
        print("yeah yeah")
    }
}

B.a() // yeah yeah
A.a() // yeah
B().getInnerValue() // yeah yeah
A().getInnerValue() // yeah
Avi Cohen
fonte