Obter nome da classe do objeto como string no Swift

320

Obtendo o nome da classe de um objeto como Stringusando:

object_getClassName(myViewController)

retorna algo como isto:

_TtC5AppName22CalendarViewController

Eu estou procurando a pura versão: "CalendarViewController". Como obtenho uma sequência limpa do nome da classe?

Encontrei algumas tentativas de perguntas sobre isso, mas não uma resposta real. Não é possível?

Bernd
fonte
como alternativa ... como seria um analisador eficaz para isso?
Bernd
Caso deseje verificar se um objeto é de uma determinada classe, consulte esta resposta .
Significado-importa

Respostas:

529

String de uma instância :

String(describing: YourType.self)

String de um tipo :

String(describing: self)

Exemplo:

struct Foo {

    // Instance Level
    var typeName: String {
        return String(describing: Foo.self)
    }

    // Instance Level - Alternative Way
    var otherTypeName: String {
        let thisType = type(of: self)
        return String(describing: thisType)
    }

    // Type Level
    static var typeName: String {
        return String(describing: self)
    }

}

Foo().typeName       // = "Foo"
Foo().otherTypeName  // = "Foo"
Foo.typeName         // = "Foo"

Testado com class, structe enum.

Rudolf Adamkovič
fonte
4
É este realmente o caso? De acordo com os documentos de String para este inicializador "um resultado não especificado é fornecido automaticamente pela biblioteca padrão Swift" quando não está em conformidade com Streamable, CustomStringConvertible ou CustomDebugStringConvertible. Portanto, embora possa estar exibindo o valor desejado agora, continuará a fazê-lo no futuro?
Tod Cunningham
3
Usar isso no Xcode 7.3.1 e no Swift 2.2 produz o seguinte, o que não é o ideal. '' let name = String (self) name String "<MyAppName.MyClassName: 0x ########>" ""
CodeBender
13
No Swift 3, a resposta correta é ligar String(describing: MyViewController.self)conforme observado em algumas respostas abaixo.
AmitaiB
10
Mas ele retorna "MyViewController.TYPE"!
Orkenstein
3
@ RudolfAdamkovič Sim, isso seria verdade. Mas eu não gosto de escrever o nome da classe explicitamente (E se eu mudar o nome da classe Fooa Foo2, mas criar uma classe Fooem outro lugar, que freio força o código). No caso de subclassificação, você precisa usar uma type(of:)ou mais, Type.selfdependendo da sua necessidade. Provavelmente type(of:)é mais apropriado para obter o nome da classe.
Marián Černý
182

ATUALIZADO PARA SWIFT 5

Podemos obter descrições bonitas de nomes de tipos usando a variável de instância através do Stringinicializador e criar novos objetos de uma determinada classe

Como, por exemplo print(String(describing: type(of: object))). Onde objectpode ser uma variável de instância como matriz, dicionário, an Int, a NSDateetc.

Como NSObjecté a classe raiz da maioria das hierarquias de classe Objective-C, você pode tentar fazer uma extensão para NSObjectobter o nome da classe de cada subclasse de NSObject. Como isso:

extension NSObject {
    var theClassName: String {
        return NSStringFromClass(type(of: self))
    }
}

Ou você pode criar uma função estática, cujo parâmetro é do tipo Any(O protocolo ao qual todos os tipos estão implicitamente em conformidade) e retorna o nome da classe como String. Como isso:

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

Agora você pode fazer algo assim:

class ClassOne : UIViewController{ /* some code here */ }
class ClassTwo : ClassOne{ /* some code here */ }

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Get the class name as String
        let dictionary: [String: CGFloat] = [:]
        let array: [Int] = []
        let int = 9
        let numFloat: CGFloat = 3.0
        let numDouble: Double = 1.0
        let classOne = ClassOne()
        let classTwo: ClassTwo? = ClassTwo()
        let now = NSDate()
        let lbl = UILabel()

        print("dictionary: [String: CGFloat] = [:] -> \(Utility.classNameAsString(dictionary))")
        print("array: [Int] = [] -> \(Utility.classNameAsString(array))")
        print("int = 9 -> \(Utility.classNameAsString(int))")
        print("numFloat: CGFloat = 3.0 -> \(Utility.classNameAsString(numFloat))")
        print("numDouble: Double = 1.0 -> \(Utility.classNameAsString(numDouble))")
        print("classOne = ClassOne() -> \((ClassOne).self)") //we use the Extension
        if classTwo != nil {
            print("classTwo: ClassTwo? = ClassTwo() -> \(Utility.classNameAsString(classTwo!))") //now we can use a Forced-Value Expression and unwrap the value
        }
        print("now = Date() -> \(Utility.classNameAsString(now))")
        print("lbl = UILabel() -> \(String(describing: type(of: lbl)))") // we use the String initializer directly

    }
}

Além disso, uma vez que podemos obter o nome da classe como String, podemos instanciar novos objetos dessa classe :

// Instantiate a class from a String
print("\nInstantiate a class from a String")
let aClassName = classOne.theClassName
let aClassType = NSClassFromString(aClassName) as! NSObject.Type
let instance = aClassType.init() // we create a new object
print(String(cString: class_getName(type(of: instance))))
print(instance.self is ClassOne)

Talvez isso ajude alguém por aí!

mauricioconde
fonte
2
Essa deve ser a resposta aceita. Obrigado, eu estava procurando uma maneira de imprimir o nome da classe sem ter que instancia-lo e encontrei a resposta aqui. Obrigado print (String (BaseAsyncTask))
Bruno Coelho
4
classNameAsStringpode ser simplificado className, pois esse "nome" implicava seu tipo. theClassNameé semelhante à história do facebook, chamada originalmente de thefacebook.
precisa
"diccionary" ... a versão em italiano da coleção de dicionário da Swift:]
Andrew Kirna 26/06/19
Esta solução inclui o nome do projeto para cada classTag, ou seja ProjectTest.MyViewController. Como faço para me livrar desse nome de projeto? Eu quero apenas o nome da classe.
Sazzad Hissain Khan 01/11/19
132

Swift 5

Aqui está a extensão para obter a typeNamevariável (trabalhe com o tipo de valor ou tipo de referência).

protocol NameDescribable {
    var typeName: String { get }
    static var typeName: String { get }
}

extension NameDescribable {
    var typeName: String {
        return String(describing: type(of: self))
    }

    static var typeName: String {
        return String(describing: self)
    }
}

Como usar:

// Extend with class/struct/enum...
extension NSObject: NameDescribable {}
extension Array: NameDescribable {}
extension UIBarStyle: NameDescribable { }

print(UITabBarController().typeName)
print(UINavigationController.typeName)
print([Int]().typeName)
print(UIBarStyle.typeName)

// Out put:
UITabBarController
UINavigationController
Array<Int>
UIBarStyle
nahung89
fonte
12
Alguma idéia de por que eu recebo MyAppName.MyClassName com isso? Eu só tenho interesse em MyClassName
Andrew
@ Andrew você poderia dar mais específico no seu caso? Estou usando esta extensão em muitos projetos e todos eles respondem como pretendido.
Nahung89 28/11
1
Bem, se eu chamá-lo da própria classe, ele retornará MyClassName, mas se eu o envolver em um método que retorna a string do nome da classe e a chamar de outra classe, ele retornará MyAppName.MyClassName. Durante a pesquisa, vi que todas as classes estão implicitamente com namespace, portanto, se você quiser chamá-lo fora da sua classe, receberá MyAppName.MyClassName em vez de MyClassName. Então você precisa confiar na manipulação de strings para obter o nome da classe.
Andrew
1
extensão muito útil
Hattori Hanzō
@ Andrew, não tenho certeza do trabalho atual, mas eu lembro que String (descrevendo: type (of: self)) usada para retornar ModuleName.ClassName em um ponto. Eu tinha me dividido com base. personagem e buscou o último objeto.
BangOperator 21/06/19
31

Swift 3.0

String(describing: MyViewController.self)

fino
fonte
2
Na verdade type(of: )é um exagero lá, String(describing: MyViewController.self)será suficiente
Alexander Vasenin
2
Isso cria uma string como <App_Name.ClassName: 0x79e14450> para mim, que não é ideal
Doug Amos
Não é String.init(describing: MyViewController.self)?
Iulian Onofrei 5/11
2
@IulianOnofrei, você pode fazer isso, mas isso inité desnecessário.
shim
Livre-se totalmente do prefixo AppName! Graças
yuchen
28

Sugiro tal abordagem ( muito Swifty ):

// Swift 3
func typeName(_ some: Any) -> String {
    return (some is Any.Type) ? "\(some)" : "\(type(of: some))"
}

// Swift 2
func typeName(some: Any) -> String {
    return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)"
}

Ele não usa nem introspecção nem desmontagem manual ( sem mágica! ).


Aqui está uma demonstração:

// Swift 3

import class Foundation.NSObject

func typeName(_ some: Any) -> String {
    return (some is Any.Type) ? "\(some)" : "\(type(of: some))"
}

class GenericClass<T> {
    var x: T? = nil
}

protocol Proto1 {
    func f(x: Int) -> Int
}


@objc(ObjCClass1)
class Class1: NSObject, Proto1 {
    func f(x: Int) -> Int {
        return x
    }
}

struct Struct1 {
    var x: Int
}

enum Enum1 {
    case X
}

print(typeName(GenericClass<Int>.self)) // GenericClass<Int>
print(typeName(GenericClass<Int>()))  // GenericClass<Int>

print(typeName(Proto1.self)) // Proto1

print(typeName(Class1.self))   // Class1
print(typeName(Class1())) // Class1
print(typeName(Class1().f)) // (Int) -> Int

print(typeName(Struct1.self)) // Struct1
print(typeName(Struct1(x: 1))) // Struct1
print(typeName(Enum1.self)) // Enum1
print(typeName(Enum1.X)) // Enum1
mergulhador
fonte
Eles mudaram no Swift 3.0. Agora você pode obter uma string detype(of: self)
Asad Ali
Atualizado para Swift 3.
werediver 11/03/19
Eu acho que essa é a melhor maneira de obter o nome do tipo: qualquer instância da classe, com base no NSObject, enum, typealiase, funções, propriedades var e quaisquer tipos, mesmo constantes. Para tipos, você precisa usar .self. Por exemplo, digite Nome (Int.self), digite Nome (true.self). Nem precisa se preocupar com o nome do projeto como parte (como AppProj. [Typename]) Ótimo!
David.Chu.ca
23

Se você digitar Foo, o código a seguir fornecerá o "Foo"Swift 3 e o Swift 4:

let className = String(describing: Foo.self) // Gives you "Foo"

O problema com a maioria das respostas aqui é que elas fornecem "Foo.Type"a sequência resultante quando você não tem nenhuma instância do tipo, quando o que você realmente deseja é justo "Foo". A seguir "Foo.Type", como mencionado em várias outras respostas.

let className = String(describing: type(of: Foo.self)) // Gives you "Foo.Type"

A type(of:)peça é desnecessária se você apenas quiser "Foo".

sethfri
fonte
21

No Swift 4.1 e agora no Swift 4.2 :

import Foundation

class SomeClass {
    class InnerClass {
        let foo: Int

        init(foo: Int) {
            self.foo = foo
        }
    }

    let foo: Int

    init(foo: Int) {
        self.foo = foo
    }
}

class AnotherClass : NSObject {
    let foo: Int

    init(foo: Int) {
        self.foo = foo
        super.init()
    }
}

struct SomeStruct {
    let bar: Int

    init(bar: Int) {
        self.bar = bar
    }
}

let c = SomeClass(foo: 42)
let s = SomeStruct(bar: 1337)
let i = SomeClass.InnerClass(foo: 2018)
let a = AnotherClass(foo: 1<<8)

Se você não tem uma instância por perto:

String(describing: SomeClass.self) // Result: SomeClass
String(describing: SomeStruct.self) // Result: SomeStruct
String(describing: SomeClass.InnerClass.self) // Result: InnerClass
String(describing: AnotherClass.self) // Result: AnotherClass

Se você tem uma instância em torno de:

String(describing: type(of: c)) // Result: SomeClass
String(describing: type(of: s)) // Result: SomeStruct
String(describing: type(of: i)) // Result: InnerClass
String(describing: type(of: a)) // Result: AnotherClass
McNight
fonte
14

Para obter o nome de uma classe Swift de um objeto, por exemplo, para o objeto var: SomeClass (), use

String(describing: type(of: object))

Para obter o nome de uma classe Swift de um tipo de classe, por exemplo, SomeClass, use:

String(describing: SomeClass.self)

Resultado:

"SomeClass"

Gurjit Singh
fonte
13

Você pode tentar desta maneira:

self.classForCoder.description()
陈方涛
fonte
Isso funciona muito bem, pois inclui o "espaço para nome" completo (o prefixo de associação de destino apropriado) também.
BonanzaDriver
BTW, eu também encontrei usando "String (self)" onde self é uma instância do MyViewController também funciona ... ele fornece as mesmas informações ... Xcode 7.1.
BonanzaDriver
12

Você pode usar a função de biblioteca padrão Swift chamada _stdlib_getDemangledTypeNameassim:

let name = _stdlib_getDemangledTypeName(myViewController)
Jernej Strasner
fonte
@NeilJaff, o que você quer dizer com "apenas uma string opcional"? Retorna uma string e não é opcional.
Jernej Strasner
3
Essa abordagem não retorna os nomes das classes nas APIs privadas FYI. Retornará o nome do primeiro nome público da classe base.
Tylerc230 15/09/2015
Recebo a mensagem: Uso do identificador não resolvido '_stdlib_getDemangledTypeName'
Adobels 5/19/19
12

Para obter o nome do tipo como uma string no Swift 4 (não verifiquei as versões anteriores), basta usar a interpolação de string:

"\(type(of: myViewController))"

Você pode usar .selfem um tipo em si e a type(of:_)função em uma instância:

// Both constants will have "UIViewController" as their value
let stringFromType = "\(UIViewController.self)"
let stringFromInstance = "\(type(of: UIViewController()))"
Misha Karpenko
fonte
8

Pode-se também usar espelhos:

let vc = UIViewController()

String(Mirror(reflecting: vc).subjectType)

NB: Este método também pode ser usado para estruturas e enums. Existe um displayStyle que fornece uma indicação de que tipo de estrutura:

Mirror(reflecting: vc).displayStyle

O retorno é uma enumeração para que você possa:

Mirror(reflecting: vc).displayStyle == .Class
Paul Ardeleanu
fonte
Obrigado. Foi o que funcionou para mim. A única coisa (pelo menos no iOS 9) é que o subjectType estava terminando em ".Type", então tive que remover essa parte da string.
Nikolay Suvandzhiev 6/06/16
8

Swift 5.1

Você pode obter nomes de classe, estrutura, enumeração, protocolo e NSObject Self.self.

print("\(Self.self)")
Victor Kushnerov
fonte
Observe que isso pode preceder o nome do módulo (diferente de String(describing: type(of: self)))
Ixx 04/04
7

Swift 3.0: você pode criar uma extensão como esta. Devolve o nome da classe sem o nome do projeto

extension NSObject {
    var className: String {
        return NSStringFromClass(self as! AnyClass).components(separatedBy: ".").last ?? ""
    }

    public class var className: String {
        return NSStringFromClass(self).components(separatedBy: ".").last ?? ""
    }
}
Exceção
fonte
Quando tento usar isso, recebo o erro: Não foi possível converter o valor do tipo 'ProjectName.MyViewController' (0x1020409e8) para 'Swift.AnyObject.Type' (0x7fb96fc0dec0).
precisa saber é o seguinte
6

Você pode estender NSObjectProtocolno Swift 4 assim:

import Foundation

extension NSObjectProtocol {

    var className: String {
        return String(describing: Self.self)
    }
}

Isso tornará a variável calculada classNamedisponível para TODAS as classes. Usar isso dentro de um print()in CalendarViewControllerserá impresso "CalendarViewController"no console.

Siddharth Bhatt
fonte
4

Eu tenho procurado esta resposta de vez em quando por um tempo. Eu uso GKStateMachine e gosto de observar alterações de estado e queria uma maneira fácil de ver apenas o nome da classe. Não tenho certeza se é apenas o iOS 10 ou o Swift 2.3, mas nesse ambiente, o seguinte faz exatamente o que eu quero:

let state:GKState?
print("Class Name: \(String(state.classForCoder)")

// Output:    
// Class Name: GKState
Troy
fonte
4

Você pode obter o nome da classe fazendo algo como:

class Person {}
String(describing: Person.self)
Ale Mohamad
fonte
3

Para obter o nome da classe como String, declare sua classe da seguinte maneira

@objc(YourClassName) class YourClassName{}

E obtenha o nome da classe usando a seguinte sintaxe

NSStringFromClass(YourClassName)
Moin Uddin
fonte
3

Experimente reflect().summarya classe própria ou a instância dynamicType. Desembrulhe os opcionais antes de obter dynamicType, caso contrário, o dynamicType é o wrapper opcional.

class SampleClass { class InnerClass{} }
let sampleClassName = reflect(SampleClass.self).summary;
let instance = SampleClass();
let instanceClassName = reflect(instance.dynamicType).summary;
let innerInstance = SampleClass.InnerClass();
let InnerInstanceClassName = reflect(innerInstance.dynamicType).summary.pathExtension;
let tupleArray = [(Int,[String:Int])]();
let tupleArrayTypeName = reflect(tupleArray.dynamicType).summary;

O resumo é um caminho de classe com os tipos genéricos descritos. Para obter um nome de classe simples do resumo, tente este método.

func simpleClassName( complexClassName:String ) -> String {
    var result = complexClassName;
    var range = result.rangeOfString( "<" );
    if ( nil != range ) { result = result.substringToIndex( range!.startIndex ); }
    range = result.rangeOfString( "." );
    if ( nil != range ) { result = result.pathExtension; }
    return result;
}
desenhado
fonte
3

As soluções acima não funcionaram para mim. O produzido principalmente as questões mencionadas em vários comentários:

MyAppName.ClassName

ou

MyFrameWorkName.ClassName

Essas soluções funcionaram no XCode 9, Swift 3.0:

Eu o nomeei classNameCleanedpara facilitar o acesso e não entrar em conflito com className()alterações futuras :

extension NSObject {

static var classNameCleaned : String {

    let className = self.className()
    if className.contains(".") {
        let namesArray = className.components(separatedBy: ".")
        return namesArray.last ?? className
    } else {
        return self.className()
    }

}

}

Uso:

NSViewController.classNameCleaned
MyCustomClass.classNameCleaned
Darkwonder
fonte
2

Swift 3.0 (macOS 10.10 e posterior), você pode obtê-lo em className

self.className.components(separatedBy: ".").last!
Thanh Vũ Trần
fonte
1
Tanto quanto eu posso dizer, não há nenhuma propriedade className interna nas classes Swift. Você subclasse de alguma coisa?
shim
@shim className é declarado no Foundation, mas parece que está disponível apenas no macOS 10.10 e posterior. Acabei de editar minha resposta.
Thanh Vũ Trần
1
Hum, isso é estranho. Por que eles não o incluíram no iOS? Observe também que é um método de instância em NSObject developer.apple.com/reference/objectivec/nsobject/…
shim
2

Eu tentei type(of:...)no Playground com o Swift 3. Esse é o meu resultado. Esta é a versão em formato de código.

print(String(describing: type(of: UIButton.self)))
print(String(describing: type(of: UIButton())))
UIButton.Type
UIButton
Lumialxk
fonte
É um desperdício instanciar uma instância apenas para determinar o nome da classe.
Sethfri
@ sethfri Eu uso isso para determinar o tipo dinâmico.
Lumialxk
Eu ainda não entendo por que você precisa para criar uma instância apenas para obter informações sobre o tipo da classe
sethfri
2

Swift 5

 NSStringFromClass(CustomClass.self)
Adobels
fonte
2

Este tipo de exemplo para a classe var. Não inclua o nome do pacote.

extension NSObject {
    class var className: String {
        return "\(self)"
    }
}
Ivan Tkachenko
fonte
1

Se você não gosta do nome mutilado, pode ditar seu próprio nome:

@objc(CalendarViewController) class CalendarViewController : UIViewController {
    // ...
}

No entanto, seria melhor a longo prazo aprender a analisar o nome mutilado. O formato é padrão e significativo e não muda.

mate
fonte
Não, não é, para a minha classe ela retorna (ExistentialMetatype) - confiar no comportamento em tempo de execução não documentado é sempre perigoso, especialmente para agilidade, pois ainda está em um estágio relativamente inicial.
superarts.org 23/09
1

Às vezes, as outras soluções dão um nome não útil, dependendo do objeto que você está tentando olhar. Nesse caso, você pode obter o nome da classe como uma string usando o seguinte.

String(cString: object_getClassName(Any!))

⌘ clique na função no xcode para ver alguns métodos relacionados que são bastante úteis. ou verifique aqui https://developer.apple.com/reference/objectivec/objective_c_functions

prumo
fonte
1

Swift 5 Você pode obter class_name como uma string com String (descrevendo: Self.self)

print(String(describing: Self.self))
yasinkbas
fonte
0

Eu uso no Swift 2.2

guard let currentController = UIApplication.topViewController() else { return }
currentController.classForCoder.description().componentsSeparatedByString(".").last!
Alexander Khitev
fonte
0

Swift 5.1: -

Você também pode usar a função genérica para obter o nome da classe do objeto como string

struct GenericFunctions {
 static func className<T>(_ name: T) -> String {
        return "\(name)"
    }

}

Chame esta função usando o seguinte: -

let name = GenericFunctions.className(ViewController.self)

Happy Coding :)

dinesh sharma
fonte
0

Swift 5.2:

String(describing: type(of: self))
Abdelaziz Elrashed
fonte