Como imprimo o tipo ou a classe de uma variável no Swift?


335

Existe uma maneira de imprimir o tipo de tempo de execução de uma variável rapidamente? Por exemplo:

var now = NSDate()
var soon = now.dateByAddingTimeInterval(5.0)

println("\(now.dynamicType)") 
// Prints "(Metatype)"

println("\(now.dynamicType.description()")
// Prints "__NSDate" since objective-c Class objects have a "description" selector

println("\(soon.dynamicType.description()")
// Compile-time error since ImplicitlyUnwrappedOptional<NSDate> has no "description" method

No exemplo acima, estou procurando uma maneira de mostrar que a variável "breve" é do tipo ImplicitlyUnwrappedOptional<NSDate>, ou pelo menos NSDate!.


43
@JasonMArcher Diga-me como isso é duplicado se a pergunta que você vinculou foi feita 4 dias após esta?
akashivskyy

Há várias perguntas sobre como testar o tipo de um objeto Swift ou ler o tipo de um objeto Swift. Estamos apenas encontrando as melhores perguntas para usar como perguntas "mestras" para esse assunto. A duplicata sugerida tem uma resposta muito mais completa. Isso não quer dizer que você fez algo errado, apenas que estamos tentando reduzir a desordem.
JasonMArcher

4
A duplicata sugerida não está respondendo à mesma pergunta; Type.self não pode ser impresso no console para fins de depuração, deve ser usado para passar para outras funções que usam Types como objetos.
Matt Bridges

2
OT: É muito estranho que o Swift não ofereça isso imediatamente e é necessário mexer nessas bibliotecas C de baixo nível. Vale um relatório de bug?
qwerty_so

Pessoal, eu forneci minha resposta abaixo. Dê uma olhada e deixe-me saber se é isso que se espera.
DILIP KOSURI

Respostas:


377

Atualização setembro de 2016

Swift 3.0: Use type(of:), por exemplo type(of: someThing)(desde que a dynamicTypepalavra-chave foi removida)

Atualização em outubro de 2015 :

Atualizei os exemplos abaixo para a nova sintaxe do Swift 2.0 (por exemplo, printlnfoi substituída por print, toString()é agora String()).

Nas notas de versão do Xcode 6.3 :

O @nschum aponta nos comentários que as notas de lançamento do Xcode 6.3 mostram outra maneira:

Os valores de tipo agora são impressos como o nome completo do tipo desmembrado quando usado com a interpolação println ou string.

import Foundation

class PureSwiftClass { }

var myvar0 = NSString() // Objective-C class
var myvar1 = PureSwiftClass()
var myvar2 = 42
var myvar3 = "Hans"

print( "String(myvar0.dynamicType) -> \(myvar0.dynamicType)")
print( "String(myvar1.dynamicType) -> \(myvar1.dynamicType)")
print( "String(myvar2.dynamicType) -> \(myvar2.dynamicType)")
print( "String(myvar3.dynamicType) -> \(myvar3.dynamicType)")

print( "String(Int.self)           -> \(Int.self)")
print( "String((Int?).self         -> \((Int?).self)")
print( "String(NSString.self)      -> \(NSString.self)")
print( "String(Array<String>.self) -> \(Array<String>.self)")

Quais saídas:

String(myvar0.dynamicType) -> __NSCFConstantString
String(myvar1.dynamicType) -> PureSwiftClass
String(myvar2.dynamicType) -> Int
String(myvar3.dynamicType) -> String
String(Int.self)           -> Int
String((Int?).self         -> Optional<Int>
String(NSString.self)      -> NSString
String(Array<String>.self) -> Array<String>

Atualização para o Xcode 6.3:

Você pode usar o _stdlib_getDemangledTypeName():

print( "TypeName0 = \(_stdlib_getDemangledTypeName(myvar0))")
print( "TypeName1 = \(_stdlib_getDemangledTypeName(myvar1))")
print( "TypeName2 = \(_stdlib_getDemangledTypeName(myvar2))")
print( "TypeName3 = \(_stdlib_getDemangledTypeName(myvar3))")

e obtenha isso como saída:

TypeName0 = NSString
TypeName1 = __lldb_expr_26.PureSwiftClass
TypeName2 = Swift.Int
TypeName3 = Swift.String

Resposta original:

Antes do Xcode 6.3, _stdlib_getTypeNamehavia o nome do tipo mutilado de uma variável. A entrada de blog de Ewan Swick ajuda a decifrar estas strings:

por exemplo, _TtSisignifica o Inttipo interno de Swift .

Mike Ash tem uma ótima entrada de blog que aborda o mesmo tópico .


2
Como você encontrou _stdlib_getTypeName()? O REPL integrado não existe mais, swift-ide-testtambém não existe, e a impressão do módulo Swift pelo Xcode _elimina os valores predefinidos.
Lily Ballard

10
Parece que eu posso pesquisar símbolos com lldbbastante facilidade no repl. Há também _stdlib_getDemangledTypeName(), e _stdlib_demangleName().
Lily Ballard

11
Oh puro, eu não sabia que imageera um atalho para target modules. Acabei usando target modules lookup -r -n _stdlib_, o que naturalmente é melhor expressa comoimage lookup -r -n _stdlib_ libswiftCore.dylib
Lily Ballard

11
FYI - Parece que toString (...) foi substituído por String (...).
Nick

6
Swift 3.0: a dynamicTypepalavra-chave foi removida do Swift. Em seu lugar, uma nova função primitiva type(of:)foi adicionado à linguagem: github.com/apple/swift/blob/master/CHANGELOG.md
Szu

46

Edit: Uma nova toStringfunção foi introduzida no Swift 1.2 (Xcode 6.3) .

Agora você pode imprimir o tipo desmembrado de qualquer tipo usando .selfe qualquer instância usando .dynamicType:

struct Box<T> {}

toString("foo".dynamicType)            // Swift.String
toString([1, 23, 456].dynamicType)     // Swift.Array<Swift.Int>
toString((7 as NSNumber).dynamicType)  // __NSCFNumber

toString((Bool?).self)                 // Swift.Optional<Swift.Bool>
toString(Box<SinkOf<Character>>.self)  // __lldb_expr_1.Box<Swift.SinkOf<Swift.Character>>
toString(NSStream.self)                // NSStream

Tente ligar YourClass.selfe yourObject.dynamicType.

Referência: https://devforums.apple.com/thread/227425 .


2
Respondi a esse tópico, mas adicionarei meu comentário aqui. Esses valores são do tipo "Metatype", mas não parece haver uma maneira fácil de descobrir que tipo de objeto um Metatype representa, e é isso que estou procurando.
Matt Bridges

Eles são do tipo Metatype e são classes. Eles representam uma classe. Não tenho certeza exatamente qual é o seu problema com isso. someInstance.dynamicType.printClassName()irá imprimir o nome da classe.
Arquivo analógico

18
o método printClassName () é apenas um exemplo de método criado em uma das amostras nos documentos, não é uma chamada de API que você pode acessar.
Dave Madeira

4
FYI - parece que toString (...) foi substituído por String (...)
Nick

33

Swift 3.0

let string = "Hello"
let stringArray = ["one", "two"]
let dictionary = ["key": 2]

print(type(of: string)) // "String"

// Get type name as a string
String(describing: type(of: string)) // "String"
String(describing: type(of: stringArray)) // "Array<String>"
String(describing: type(of: dictionary)) // "Dictionary<String, Int>"

// Get full type as a string
String(reflecting: type(of: string)) // "Swift.String"
String(reflecting: type(of: stringArray)) // "Swift.Array<Swift.String>"
String(reflecting: type(of: dictionary)) // "Swift.Dictionary<Swift.String, Swift.Int>"

11
A implementação do Swift 2 é particularmente agradável em casos de enumeração. Alguém sabe se esse comportamento é documentado / garantido pela Apple? Biblioteca padrão meramente inclui um comentário que este é apropriado para a depuração ...
Milos

30

É isso que você está procurando?

println("\(object_getClassName(now))");

Imprime "__NSDate"

ATUALIZAÇÃO : observe que isso não parece mais funcionar a partir do Beta05


2
Não funciona para aulas rápidas. Retorna "UnsafePointer (0x7FBB18D06DE0)"
aryaxt 12/08/14

4
Não funciona para as classes ObjC quer (pelo menos em Beta5), para uma NSManagedObject eu fico "Builtin.RawPointer = endereço"
Kendall Helmstetter Gelner

23

Meu Xcode atual é a versão 6.0 (6A280e).

import Foundation

class Person { var name: String; init(name: String) { self.name = name }}
class Patient: Person {}
class Doctor: Person {}

var variables:[Any] = [
    5,
    7.5,
    true,
    "maple",
    Person(name:"Sarah"),
    Patient(name:"Pat"),
    Doctor(name:"Sandy")
]

for variable in variables {
    let typeLongName = _stdlib_getDemangledTypeName(variable)
    let tokens = split(typeLongName, { $0 == "." })
    if let typeName = tokens.last {
        println("Variable \(variable) is of Type \(typeName).")
    }
}

Resultado:

Variable 5 is of Type Int.
Variable 7.5 is of Type Double.
Variable true is of Type Bool.
Variable maple is of Type String.
Variable Swift001.Person is of Type Person.
Variable Swift001.Patient is of Type Patient.
Variable Swift001.Doctor is of Type Doctor.

11
Bom, mas a pergunta também incluía Opcionais, a saída de opcionais está definida Optional(...), portanto, seu código retornaria apenas Optionalcomo o Tipo. Também deve aparecer var optionalTestVar: Person? = Person(name:"Sarah")como Opcional (Pessoa) e var com Pessoa! Como ImplicitlyUnwrappedOptional (Pessoa)
Binarian

18

A partir do Xcode 6.3 com o Swift 1.2, você pode simplesmente converter valores de tipo no total desmembrado String.

toString(Int)                   // "Swift.Int"
toString(Int.Type)              // "Swift.Int.Type"
toString((10).dynamicType)      // "Swift.Int"
println(Bool.self)              // "Swift.Bool"
println([UTF8].self)            // "Swift.Array<Swift.UTF8>"
println((Int, String).self)     // "(Swift.Int, Swift.String)"
println((String?()).dynamicType)// "Swift.Optional<Swift.String>"
println(NSDate)                 // "NSDate"
println(NSDate.Type)            // "NSDate.Type"
println(WKWebView)              // "WKWebView"
toString(MyClass)               // "[Module Name].MyClass"
toString(MyClass().dynamicType) // "[Module Name].MyClass"

A outra direção é possível no 1.2? Para inicializar uma instância com base no nome da classe?
user965972

@ user965972 Tanto quanto sei, não acho que exista.
kiding

17

Você ainda pode acessar a classe através de className(que retorna a String).

Na verdade, existem várias maneiras de obter a classe, por exemplo classForArchiver, classForCoder, classForKeyedArchiver(todos de retorno AnyClass!).

Você não pode obter o tipo de primitivo (um primitivo não é uma classe).

Exemplo:

var ivar = [:]
ivar.className // __NSDictionaryI

var i = 1
i.className // error: 'Int' does not have a member named 'className'

Se você deseja obter o tipo de primitivo, precisa usar bridgeToObjectiveC(). Exemplo:

var i = 1
i.bridgeToObjectiveC().className // __NSCFNumber

Aposto que é um primitivo. Infelizmente você não pode obter o tipo de primitivo.
Leandros

Esses exemplos parecem não funcionar no parquinho. Eu recebo o erro de primitivos e não primitivos.
Matt Bridges

tente import Cocoa.
Leandros

9
className parece estar disponível apenas para NSObjects no desenvolvimento para OSX; ele não está disponível no iOS SDK tanto para NSObjects ou objetos "normais"
Matt Pontes

3
classNamefaz parte do suporte AppleScript da Cocoa e definitivamente não deve ser usado para introspecção genérica. Obviamente, não está disponível no iOS.
Nikolai Ruhe

16

Você pode usar refletir para obter informações sobre o objeto.
Por exemplo, nome da classe de objeto:

var classname = refletir (agora). resumo

No Beta 6, isso me deu o nome do aplicativo + o nome da classe de forma um pouco confusa, mas é um começo. +1 para reflect(x).summary- obrigado!
Olie

não funcionou, imprime uma linha em branco no Swift 1,2 Xcode 6,4 e 8,4 iOs
Juan Boero

13

Eu tive sorte com:

let className = NSStringFromClass(obj.dynamicType)

@hdost cara nada funcionou, mas classForCoder funcionou.
Satheeshwaran

12

Tipo de uso do Xcode 8 Swift 3.0 (de :)

let className = "\(type(of: instance))"

10

No Xcode 8, Swift 3.0

Passos:

1. Obtenha o tipo:

Opção 1:

let type : Type = MyClass.self  //Determines Type from Class

Opção 2:

let type : Type = type(of:self) //Determines Type from self

2. Converta Tipo em String:

let string : String = "\(type)" //String

8

No Swift 3.0, você pode usar type(of:), pois a dynamicTypepalavra-chave foi removida.


7

SWIFT 5

Com a versão mais recente do Swift 3, podemos obter descrições bonitas de nomes de tipos através do Stringinicializador. Como, por exemplo print(String(describing: type(of: object))). Onde object pode haver uma variável de instância como matriz, dicionário, an Int, a NSDate, instância de uma classe personalizada etc.

Aqui está a minha resposta completa: Obter nome da classe do objeto como string no Swift

Essa pergunta está procurando uma maneira de obter o nome da classe de um objeto como string, mas também propus outra maneira de obter o nome da classe de uma variável que não é subclasse de NSObject. Aqui está:

class Utility{
    class func classNameAsString(obj: Any) -> String {
        //prints more readable results for dictionaries, arrays, Int, etc
        return String(describing: type(of: obj))
    }
}

Eu criei uma função estática que toma como parâmetro um objeto do tipo Anye retorna seu nome de classe como String:).

Eu testei essa função com algumas variáveis ​​como:

    let diccionary: [String: CGFloat] = [:]
    let array: [Int] = []
    let numInt = 9
    let numFloat: CGFloat = 3.0
    let numDouble: Double = 1.0
    let classOne = ClassOne()
    let classTwo: ClassTwo? = ClassTwo()
    let now = NSDate()
    let lbl = UILabel()

e a saída foi:

  • dicionário é do tipo Dicionário
  • matriz é do tipo Matriz
  • numInt é do tipo Int
  • numFloat é do tipo CGFloat
  • numDouble é do tipo Double
  • classOne é do tipo: ClassOne
  • classTwo é do tipo: ClassTwo
  • agora é do tipo: Data
  • lbl é do tipo: UILabel

5

Ao usar o Cocoa (não o CocoaTouch), você pode usar a classNamepropriedade para objetos que são subclasses de NSObject.

println(now.className)

Essa propriedade não está disponível para objetos Swift normais, que não são subclasses de NSObject (e, de fato, não há ID de raiz ou tipo de objeto no Swift).

class Person {
    var name: String?
}

var p = Person()
println(person.className) // <- Compiler error

No CocoaTouch, no momento não há uma maneira de obter uma descrição de string do tipo de uma determinada variável. Uma funcionalidade semelhante também não existe para tipos primitivos no Cocoa ou no CocoaTouch.

O Swift REPL pode imprimir um resumo dos valores, incluindo seu tipo, portanto, é possível que esse tipo de introspecção seja possível por meio de uma API no futuro.

EDIT : dump(object)parece fazer o truque.


Mesmo no Xcode 10 e no Swift 4.2, dump(object)é realmente poderoso. Obrigado.
Alex Zavatone 16/05/19

5

A resposta principal não tem um exemplo prático da nova maneira de fazer isso usando type(of:. Então, para ajudar novatos como eu, aqui está um exemplo prático, obtido principalmente dos documentos da Apple aqui - https://developer.apple.com/documentation/swift/2885064-type

doubleNum = 30.1

func printInfo(_ value: Any) {
    let varType = type(of: value)
    print("'\(value)' of type '\(varType)'")
}

printInfo(doubleNum)
//'30.1' of type 'Double'

4

Eu tentei algumas das outras respostas aqui, mas a milhagem parece muito sobre qual é o objeto subjacente.

No entanto, encontrei uma maneira de obter o nome da classe Object-C para um objeto, fazendo o seguinte:

now?.superclass as AnyObject! //replace now with the object you are trying to get the class name for

Aqui está um exemplo de como você o usaria:

let now = NSDate()
println("what is this = \(now?.superclass as AnyObject!)")

Nesse caso, ele imprimirá o NSDate no console.


4

Encontrei esta solução que, esperançosamente, pode funcionar para outra pessoa. Eu criei um método de classe para acessar o valor. Lembre-se de que isso funcionará apenas na subclasse NSObject. Mas pelo menos é uma solução limpa e arrumada.

class var className: String!{
    let classString : String = NSStringFromClass(self.classForCoder())
    return classString.componentsSeparatedByString(".").last;
}

4

No mais recente XCode 6.3 com Swift 1.2, esta é a única maneira que encontrei:

if view.classForCoder.description() == "UISegment" {
    ...
}

NOTA: description () retorna o nome da classe no formato <swift module name>.<actual class name>, então você deseja dividir a string com a .e obter o último token.
Matthew Quiros

4

Muitas das respostas aqui não funcionam com o Swift mais recente (Xcode 7.1.1 no momento da redação).

A maneira atual de obter as informações é criar Mirrore interrogar isso. Para o nome da classe, é tão simples quanto:

let mirror = Mirror(reflecting: instanceToInspect)
let classname:String = mirror.description

Informações adicionais sobre o objeto também podem ser recuperadas no Mirror. Veja http://swiftdoc.org/v2.1/type/Mirror/ para obter detalhes.


O que há de errado com as respostas stackoverflow.com/a/25345480/1187415 e stackoverflow.com/a/32087449/1187415 ? Ambos parecem funcionar muito bem com Swift 2. (ainda não verificou os outros.)
Martin R

Eu gosto disso como uma solução geral, mas obter "Mirror for ViewController" ("Mirror for" estranho)) - espere, parece que usar ".subjectType" faz o truque!
David H

3

No lldb a partir do beta 5, você pode ver a classe de um objeto com o comando:

fr v -d r shipDate

que gera algo como:

(DBSalesOrderShipDate_DBSalesOrderShipDate_ *) shipDate = 0x7f859940

O comando expandido significa algo como:

Frame Variable(imprima uma variável de quadro) -d run_target(expanda tipos dinâmicos)

Algo útil a saber é que o uso de "Variável de quadro" para gerar valores de variável garante que nenhum código seja executado.


3

Encontrei uma solução para classes desenvolvidas por você (ou você tem acesso).

Coloque a seguinte propriedade computada na sua definição de classe de objetos:

var className: String? {
    return __FILE__.lastPathComponent.stringByDeletingPathExtension
}

Agora você pode simplesmente chamar o nome da classe no seu objeto da seguinte maneira:

myObject.className

Observe que isso só funcionará se sua definição de classe for feita em um arquivo nomeado exatamente como a classe da qual você deseja o nome.

Como esse é geralmente o caso, a resposta acima deve fazê-lo na maioria dos casos . Mas, em alguns casos especiais, você pode precisar descobrir uma solução diferente.


Se você precisar do nome da classe dentro da própria classe (arquivo), basta usar esta linha:

let className = __FILE__.lastPathComponent.stringByDeletingPathExtension

Talvez esse método ajude algumas pessoas por aí.


3

Com base nas respostas e comentários de Klass e Kevin Ballard acima, eu iria com:

println(_stdlib_getDemangledTypeName(now).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon?).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon!).componentsSeparatedByString(".").last!)

println(_stdlib_getDemangledTypeName(myvar0).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar1).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar2).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar3).componentsSeparatedByString(".").last!)

que será impresso:

"NSDate"
"ImplicitlyUnwrappedOptional"
"Optional"
"NSDate"

"NSString"
"PureSwiftClass"
"Int"
"Double"

Eu acho que essa é a melhor resposta.
maschall

Acabei usando no Swift 1.2 o seguinte: toString(self.dynamicType).componentsSeparatedByString(".").last!obviamente estava em um contexto de objeto e era capaz de fazer referência self.
315 Joe

3
let i: Int = 20


  func getTypeName(v: Any) -> String {
    let fullName = _stdlib_demangleName(_stdlib_getTypeName(i))
    if let range = fullName.rangeOfString(".") {
        return fullName.substringFromIndex(range.endIndex)
    }
    return fullName
}

println("Var type is \(getTypeName(i)) = \(i)")

3

Swift versão 4:

print("\(type(of: self)) ,\(#function)")
// within a function of a class

Thanks @Joshua Dance


3

Swift 4:

// "TypeName"
func stringType(of some: Any) -> String {
    let string = (some is Any.Type) ? String(describing: some) : String(describing: type(of: some))
    return string
}

// "ModuleName.TypeName"
func fullStringType(of some: Any) -> String {
    let string = (some is Any.Type) ? String(reflecting: some) : String(reflecting: type(of: some))
    return string
}

Uso:

print(stringType(of: SomeClass()))     // "SomeClass"
print(stringType(of: SomeClass.self))  // "SomeClass"
print(stringType(of: String()))        // "String"
print(fullStringType(of: String()))    // "Swift.String"

2

Parece não haver uma maneira genérica de imprimir o nome do tipo de um valor arbitrário. Como outros observaram, para instâncias de classe, você pode imprimir, value.classNamemas para valores primitivos parece que, em tempo de execução, as informações de tipo desaparecem.

Por exemplo, parece que não há como digitar: 1.something()e saia Intpor qualquer valor de something. (Você pode, como outra resposta sugerida, usar i.bridgeToObjectiveC().classNamepara dar uma dica, mas na verdade não__NSCFNumber é o tipo de - exatamente o que será convertido quando ultrapassar o limite de uma chamada de função Objective-C.)i

Eu ficaria feliz em ter provado que está errado, mas parece que a verificação de tipo é feita em tempo de compilação e, como o C ++ (com o RTTI desativado), grande parte das informações de tipo desaparece em tempo de execução.


Você pode dar um exemplo do uso de className para imprimir "NSDate" para a variável "now" no meu exemplo que funciona no playground do Xcode6? Tanto quanto sei, className não está definido nos objetos Swift, mesmo aqueles que são subclasses de NSObject.
Matt Bridges

Parece que 'className' está disponível para objetos que descendem do NSObject, mas apenas no desenvolvimento para mac, não para iOS. Parece não haver nenhuma solução alternativa para o iOS.
Matt Bridges

11
Sim, o verdadeiro argumento aqui parece ser que, no caso geral, as informações do tipo desaparecem rapidamente em tempo de execução.
Ipmcc # 1

Estou curioso para saber como o REPL é capaz de imprimir os tipos de valores.
Matt Bridges

O REPL é quase certamente interpretado e não compilado, que seria facilmente explicar isso
ipmcc

2

Isto é como você começa uma seqüência de tipo do seu objeto ou tipo que é consistente e leva em conta a que o módulo a definição do objeto pertence ou aninhado em. Obras em Swift 4.x.

@inline(__always) func typeString(for _type: Any.Type) -> String {
    return String(reflecting: type(of: _type))
}

@inline(__always) func typeString(for object: Any) -> String {
    return String(reflecting: type(of: type(of: object)))
}

struct Lol {
    struct Kek {}
}

// if you run this in playground the results will be something like
typeString(for: Lol.self)    //    __lldb_expr_74.Lol.Type
typeString(for: Lol())       //    __lldb_expr_74.Lol.Type
typeString(for: Lol.Kek.self)//    __lldb_expr_74.Lol.Kek.Type
typeString(for: Lol.Kek())   //    __lldb_expr_74.Lol.Kek.Type

2

Para obter um tipo de objeto ou classe de objeto no Swift , você precisa usar um tipo (de: yourObject)

tipo (de: seuObjeto)


1

Não é exatamente o que você procura, mas você também pode verificar o tipo da variável em relação aos tipos Swift da seguinte forma:

let object: AnyObject = 1

if object is Int {
}
else if object is String {
}

Por exemplo.


1

Xcode 7.3.1, Swift 2.2:

String(instanceToPrint.self).componentsSeparatedByString(".").last

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.