Ottieni il nome della classe dell'oggetto come stringa in Swift


320

Ottenere il nome di classe di un oggetto come Stringusando:

object_getClassName(myViewController)

restituisce qualcosa del genere:

_TtC5AppName22CalendarViewController

Sto cercando la pura versione: "CalendarViewController". Come ottengo invece una stringa pulita del nome della classe?

Ho trovato alcuni tentativi di domande su questo, ma non una risposta effettiva. Non è possibile affatto?


in alternativa ... che aspetto avrebbe un efficace parser per questo?
Bernd,

Nel caso in cui desideri verificare se un oggetto appartiene a una determinata classe, vedi questa risposta .
significato-questioni

Risposte:


529

Stringa da un'istanza :

String(describing: YourType.self)

Stringa da un tipo :

String(describing: self)

Esempio:

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"

Testato con class, structe enum.


4
È davvero così? Secondo i documenti di String per questo inizializzatore "un risultato non specificato viene fornito automaticamente dalla libreria standard Swift" quando non è conforme a Streamable, CustomStringConvertible o CustomDebugStringConvertible. Quindi, mentre potrebbe visualizzare il valore desiderato ora, continuerà a farlo in futuro?
Tod Cunningham,

3
Usarlo in Xcode 7.3.1 e Swift 2.2 produce quanto segue, il che non è l'ideale. '' let name = String (self) name String "<MyAppName.MyClassName: 0x ########>" ''
CodeBender

13
In Swift 3, la risposta corretta è chiamare String(describing: MyViewController.self)come indicato in alcune risposte di seguito.
AmitaiB,

10
Ma restituisce "MyViewController.TYPE"!
orkenstein,

3
@ RudolfAdamkovič Sì, sarebbe vero. Ma non mi piace scrivere il nome della classe in modo esplicito (Cosa succede se rinominare la classe Fooa Foo2, ma creare una classe Fooaltrove, che potrebbe freno il codice). In caso di sottoclasse è necessario utilizzare uno type(of:)o Type.selfsecondo ciò che si adatta alle proprie esigenze. Probabilmente type(of:)è più appropriato per ottenere il nome della classe.
Marián Černý

182

AGGIORNATO A SWIFT 5

Possiamo ottenere belle descrizioni dei nomi dei tipi usando la variabile di istanza attraverso l' Stringinizializzatore e creare nuovi oggetti di una certa classe

Ad esempio print(String(describing: type(of: object))). Dove objectpuò essere una variabile di istanza come array, un dizionario, an Int, a NSDate, ecc.

Poiché NSObjectè la classe radice della maggior parte delle gerarchie di classi Objective-C, è possibile provare a creare un'estensione per NSObjectottenere il nome della classe di ogni sottoclasse di NSObject. Come questo:

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

Oppure potresti creare una funzione statica il cui parametro è di tipo Any(il protocollo al quale tutti i tipi sono implicitamente conformi) e restituisce il nome della classe come String. Come questo:

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

Ora puoi fare qualcosa del genere:

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

    }
}

Inoltre, una volta che possiamo ottenere il nome della classe come String, possiamo creare un'istanza di nuovi oggetti di quella 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)

Forse questo aiuta qualcuno là fuori !.


2
Questa dovrebbe essere la risposta accettata. Grazie, stavo cercando un modo per stampare il nome della classe senza doverlo istanziare e ho trovato la risposta qui. Grazie stampa (String (BaseAsyncTask))
Bruno Coelho,

4
classNameAsStringpuò essere semplificato className, poiché quel "nome" implicava il suo tipo. theClassNameè simile alla storia di Facebook, che in origine si chiama thefacebook.
DawnSong,

"diccionary" ... la versione italiana della collezione di dizionari Swift:]
Andrew Kirna,

Questa soluzione antepone il nome del progetto per ogni classTag, ad es ProjectTest.MyViewController. Come mi libero di questo nome di progetto? Voglio solo il nome della classe.
Sazzad Hissain Khan,

132

Swift 5

Ecco l'estensione per ottenere la typeNamevariabile come (funziona sia con il valore che con il tipo di riferimento).

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

Come usare:

// 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

12
Qualche idea sul perché ottengo MyAppName.MyClassName da questo? Sono solo interessato a MyClassName
Andrew,

@Andrew potresti dare più specifici nel tuo caso? Sto usando questa estensione in molti progetti e tutti rispondono come previsto.
nahung89,

1
Bene, se lo chiamo dalla classe stessa, restituisce MyClassName, ma se lo avvolgo in un metodo che restituisce la stringa del nome della classe e la chiamo da un'altra classe, restituisce MyAppName.MyClassName. Durante la ricerca ho visto che tutte le classi hanno implicitamente lo spazio dei nomi, quindi se vuoi chiamarlo al di fuori della tua classe ottieni MyAppName.MyClassName invece di MyClassName. Quindi devi fare affidamento sulla manipolazione delle stringhe per ottenere il nome della classe.
Andrew,

1
estensione molto utile
Hattori Hanzō

@Andrew, non sono sicuro del funzionamento attuale, ma ricordo, String (descrivendo: tipo (di: auto)) usato per restituire ModuleName.ClassName in un punto. Mi ero diviso in base a. personaggio e recuperato l'ultimo oggetto.
BangOperator,

31

Swift 3.0

String(describing: MyViewController.self)


2
In realtà type(of: )lì è un eccesso, String(describing: MyViewController.self)basterà
Alexander Vasenin il

2
Questo crea una stringa come <App_Name.ClassName: 0x79e14450> per me, che non è l'ideale
Doug Amos,

No String.init(describing: MyViewController.self)?
Iulian Onofrei il

2
@IulianOnofrei, puoi farlo, ma initnon è necessario.
shim

Sbarazzati del prefisso AppName! Grazie
yuchen il

28

Suggerisco un tale approccio ( molto 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)"
}

Non usa né introspezione né demangling manuale ( nessuna magia! ).


Ecco una demo:

// 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

L'hanno cambiato in Swift 3.0. Ora puoi ottenere la stringa datype(of: self)
Asad Ali

Aggiornato a Swift 3.
werediver l'

Penso che questo sia il modo migliore per ottenere il nome del tipo: qualsiasi istanza di classe, basata su NSObject, enum, typealiase, funzioni, proprietà var e qualsiasi tipo, anche costanti. Per i tipi, devi usare. Stesso. Ad esempio, typeName (Int.self), typeName (true.self). Né devi preoccuparti del nome del progetto come parte (come AppProj. [Typename]) Fantastico!
David.Chu.ca,

23

Se hai digitato Foo, il seguente codice ti darà "Foo"in Swift 3 e Swift 4:

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

Il problema con la maggior parte delle risposte qui è che ti danno "Foo.Type"come stringa risultante quando non hai alcuna istanza del tipo, quando quello che vuoi davvero è giusto "Foo". Quanto segue ti dà "Foo.Type", come menzionato in un gruppo di altre risposte.

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

La type(of:)parte non è necessaria se si desidera "Foo".


21

In Swift 4.1 e ora 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 non hai un'istanza in giro:

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 hai un'istanza in giro:

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

14

Per ottenere il nome di una classe Swift da un oggetto, ad es. Per oggetto var: SomeClass (), utilizzare

String(describing: type(of: object))

Per ottenere il nome di una classe Swift da un tipo di classe, ad esempio SomeClass, utilizzare:

String(describing: SomeClass.self)

Produzione:

"SomeClass"


13

Puoi provare in questo modo:

self.classForCoder.description()

Funziona alla grande poiché include anche lo "spazio dei nomi" completo (il prefisso di appartenenza di destinazione appropriato).
BonanzaDriver

A proposito, ho anche trovato usando "String (self)" dove self è un'istanza di MyViewController funziona anche ... fornisce le stesse informazioni ... Xcode 7.1.
BonanzaDriver,

12

Puoi usare la funzione di libreria standard Swift chiamata _stdlib_getDemangledTypeNamecosì:

let name = _stdlib_getDemangledTypeName(myViewController)

@NeilJaff cosa intendi con "solo una stringa opzionale"? Restituisce una stringa e non è facoltativo.
Jernej Strasner,

3
Questo approccio non restituisce i nomi delle classi nell'API privata FYI. Restituirà il nome del primo nome della classe di base pubblica.
Tylerc230,

Ricevo il messaggio: uso dell'identificatore irrisolto '_stdlib_getDemangledTypeName'
Adobels

12

Per ottenere il nome del tipo come stringa in Swift 4 (non ho verificato le versioni precedenti), basta usare l'interpolazione di stringa:

"\(type(of: myViewController))"

È possibile utilizzare .selfsu un tipo stesso e la type(of:_)funzione su un'istanza:

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

8

Si possono anche usare specchi:

let vc = UIViewController()

String(Mirror(reflecting: vc).subjectType)

NB: questo metodo può essere utilizzato anche per Struct ed Enums. C'è un displayStyle che fornisce un'indicazione del tipo di struttura:

Mirror(reflecting: vc).displayStyle

Il ritorno è un enum quindi puoi:

Mirror(reflecting: vc).displayStyle == .Class

Grazie. Questo è ciò che ha funzionato per me. L'unica cosa (almeno su iOS 9) è che subjectType stava finendo in ".Type", quindi ho dovuto rimuovere quella parte dalla stringa.
Nikolay Suvandzhiev il

8

Swift 5.1

Tuttavia, puoi ottenere nomi di classi, struct, enum, protocol e NSObject Self.self.

print("\(Self.self)")

Si noti che ciò può anteporre al nome del modulo (diversamente da String(describing: type(of: self)))
Ixx

7

Swift 3.0: puoi creare un'estensione come questa .. Restituisce il nome della classe senza il nome del progetto

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

    public class var className: String {
        return NSStringFromClass(self).components(separatedBy: ".").last ?? ""
    }
}

Quando provo a utilizzare questo, visualizzo l'errore: Impossibile eseguire il cast del valore di tipo 'ProjectName.MyViewController' (0x1020409e8) su 'Swift.AnyObject.Type' (0x7fb96fc0dec0).
tylerSF,

6

Puoi estendere NSObjectProtocolin Swift 4 in questo modo:

import Foundation

extension NSObjectProtocol {

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

Ciò renderà la variabile calcolata classNamedisponibile per TUTTE le classi. Usando questo all'interno di un print()in CalendarViewControllerverrà stampato"CalendarViewController" in console.


4

Ho cercato questa risposta a intermittenza per un po '. Uso GKStateMachine e mi piace osservare i cambiamenti di stato e volevo un modo semplice per vedere solo il nome della classe. Non sono sicuro che si tratti solo di iOS 10 o Swift 2.3, ma in quell'ambiente, ciò che segue fa esattamente quello che voglio:

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

// Output:    
// Class Name: GKState

4

Puoi ottenere il nome della classe facendo qualcosa del tipo:

class Person {}
String(describing: Person.self)

3

Per ottenere il nome della classe come String, dichiarare la classe come segue

@objc(YourClassName) class YourClassName{}

E ottieni il nome della classe usando la sintassi seguente

NSStringFromClass(YourClassName)

3

Prova reflect().summarysu Classe self o istanza dynamicType. Scartare gli optional prima di ottenere dynamicType, altrimenti dynamicType è il wrapper opzionale.

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;

Il riepilogo è un percorso di classe con i tipi generici descritti. Per ottenere un semplice nome di classe dal riepilogo, provare questo metodo.

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;
}

3

Le soluzioni di cui sopra non hanno funzionato per me. Ciò ha prodotto principalmente i problemi menzionati in diversi commenti:

MyAppName.ClassName

o

MyFrameWorkName.ClassName

Questa soluzione ha funzionato su XCode 9, Swift 3.0:

L'ho chiamato classNameCleanedcosì è più facile accedere e non è in conflitto con le className()modifiche future :

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

2

Swift 3.0 (macOS 10.10 e versioni successive), puoi ottenerlo da className

self.className.components(separatedBy: ".").last!

1
Per quanto ne so, non esiste alcuna proprietà className incorporata nelle classi Swift. Hai fatto una sottoclasse da qualcosa?
shim

@shim className è dichiarato in Foundation ma sembra che sia disponibile solo su macOS 10.10 e versioni successive. Ho appena modificato la mia risposta.
Thanh Vũ Trần,

1
È strano. Perché non dovrebbero includerlo in iOS? Si noti inoltre che è un metodo di istanza su NSObject developer.apple.com/reference/objectivec/nsobject/…
shim

2

Ho provato type(of:...)in Playground con Swift 3. Questo è il mio risultato. Questa è la versione in formato codice.

print(String(describing: type(of: UIButton.self)))
print(String(describing: type(of: UIButton())))
UIButton.Type
UIButton

È inutile istanziare un'istanza solo per determinare il nome della classe.
sethfri,

@sethfri Lo uso per determinare il tipo dinamico.
Lumialxk,

Non capisco ancora perché è necessario creare un'istanza solo per ottenere le informazioni sul tipo della classe
sethfri,


2

Questo tipo di esempio per la classe var. Non includere il nome del pacchetto.

extension NSObject {
    class var className: String {
        return "\(self)"
    }
}

1

Se non ti piace il nome alterato, puoi dettare il tuo nome:

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

Tuttavia, a lungo andare sarebbe meglio imparare a analizzare il nome maledetto. Il formato è standard e significativo e non cambierà.


No non lo è, per la mia classe restituisce (ExistentialMetatype) - fare affidamento su un comportamento di runtime non documentato è sempre pericoloso, soprattutto per il rapido poiché è ancora in una fase relativamente precoce.
superarts.org,

1

A volte le altre soluzioni daranno un nome non utile a seconda dell'oggetto che stai cercando di guardare. In tal caso, puoi ottenere il nome della classe come stringa usando quanto segue.

String(cString: object_getClassName(Any!))

⌘ fai clic sulla funzione in xcode per vedere alcuni metodi correlati che sono abbastanza utili. oppure controlla qui https://developer.apple.com/reference/objectivec/objective_c_functions


1

Swift 5 Puoi ottenere class_name come stringa con String (descrivendo: Self.self)

print(String(describing: Self.self))

0

Lo uso in Swift 2.2

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

0

Swift 5.1: -

È inoltre possibile utilizzare la funzione generica per ottenere il nome classe dell'oggetto come stringa

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

}

Chiamare questa funzione usando quanto segue: -

let name = GenericFunctions.className(ViewController.self)

Happy Coding :)


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.