web-dev-qa-db-fra.com

Récupère le nom de classe de l'objet sous forme de chaîne dans Swift

Obtenir le nom de classe d'un objet sous forme de chaîne à l'aide de

object_getClassName(myViewController)

retourne quelque chose comme ça

_TtC5AppName22CalendarViewController

Je cherche la version pure : "CalendarViewController". Comment puis-je obtenir un nettoyé chaîne du nom de la classe? 

J'ai trouvé quelques tentatives de questions à ce sujet mais pas une réponse réelle. N'est-ce pas possible?

238
Bernd

Chaîne d'un instance:

String(describing: YourType.self)

Chaîne d'un type:

String(describing: self)

Exemple:

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"

Testé avec class, struct et enum.

426
Rudolf Adamkovič

MISE À JOUR VERS 4.2

On peut obtenir de jolies descriptions des noms de types en utilisant la variable d'instance via l'initialiseur String et créer de nouveaux objets d'une certaine classe 

J'aime, par exemple print(String(describing: type(of: object))). Où object peut être un variable d'instance comme un tableau, un dictionnaire, une Int, une NSDate, etc.

Comme NSObject est la classe racine de la plupart des hiérarchies de classes Objective-C, vous pouvez essayer de créer une extension pour NSObject afin d’obtenir le nom de classe de chaque sous-classe de NSObject. Comme ça:

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

Vous pouvez également créer une fonction statique dont le paramètre est de type Any (protocole auquel tous les types se conforment implicitement) et renvoyer le nom de la classe sous forme de chaîne. Comme ça:

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

Maintenant, vous pouvez faire quelque chose comme ça:

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("diccionary: [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

    }
}

De plus, une fois que nous pouvons obtenir le nom de la classe sous la forme String, nous pouvons instancier de nouveaux objets de cette classe:

// Instanciate a class from a String
print("\nInstaciate 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)

Peut-être que cela aide quelqu'un là-bas!.

117
mauricioconde

Swift 4.2

Voici l'extension pour obtenir la variable typeName en tant que variable (pour le type de valeur ou le type de référence).

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

Comment utiliser:

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

print(UITabBarController().typeName)
print(UINavigationController.typeName)
print([Int]().typeName)
103
nahung89

Je suggère une telle approche ( very 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)"
}

Il n’utilise ni introspection ni démêlage manuel ( pas de magie! ).


Voici une démo:

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

Si vous avez le type Foo, le code suivant vous donnera "Foo" dans Swift 3 et Swift 4:

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

Le problème avec la plupart des réponses ici est qu’elles vous donnent "Foo.Type" comme chaîne résultante lorsque vous n’avez aucune instance du type, alors que ce que vous voulez vraiment est juste "Foo". Ce qui suit vous donne "Foo.Type", comme mentionné dans un tas d’autres réponses.

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

La partie type(of:) est inutile si vous voulez juste "Foo".

20
sethfri

Dans Swift 4.1 et maintenant 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)

Si vous n'avez pas d'instance autour:

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

Si vous avez une instance autour 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
14
McNight

Vous pouvez utiliser la fonction de bibliothèque standard Swift appelée _stdlib_getDemangledTypeName comme ceci:

let name = _stdlib_getDemangledTypeName(myViewController)
12
Jernej Strasner

Pour obtenir le nom du type sous forme de chaîne dans Swift 4 (je n'ai pas vérifié les versions précédentes), utilisez simplement une interpolation de chaîne:

"\(type(of: myViewController))"

Vous pouvez utiliser .self sur un type lui-même et la fonction type(of:_) sur une instance:

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

Vous pouvez essayer de cette façon:

self.classForCoder.description()
8
陈方涛

On peut aussi utiliser des miroirs:

let vc = UIViewController()

String(Mirror(reflecting: vc).subjectType)

NB: Cette méthode peut également être utilisée pour Structs et Enums. Il y a un displayStyle qui donne une indication de quel type de la structure:

Mirror(reflecting: vc).displayStyle

Le retour est une énumération afin que vous puissiez:

Mirror(reflecting: vc).displayStyle == .Class
7
Paul Ardeleanu

Swift 3.0: Vous pouvez créer une extension comme celle-ci .. Elle renvoie le nom de la classe sans le nom du projet.

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

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

Je cherche cette réponse par intermittence depuis un moment. J'utilise GKStateMachine et j'aime observer les changements d'état et je voulais un moyen facile de voir uniquement le nom de la classe. Je ne sais pas s'il s'agit uniquement de iOS 10 ou de Swift 2.3, mais dans cet environnement, les éléments suivants remplissent exactement ce que je veux:

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

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

Essayez reflect().summary sur la classe self ou l’instance dynamicType. Décompressez les options avant d’obtenir dynamicType, sinon, dynamicType est l’encapsuleur facultatif.

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;

Le résumé est un chemin de classe avec les types génériques décrits. Pour obtenir un nom de classe simple à partir du résumé, essayez cette méthode.

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

Les solutions ci-dessus ne fonctionnaient pas pour moi. Le produit principalement les questions mentionnent dans plusieurs commentaires:

MonNomApp.NomClasse

ou

MyFrameWorkName.ClassName

Cette solution fonctionnait sous XCode 9, Swift 3.0:

Je l'ai nommé classNameCleaned pour qu'il soit plus facile d'accès et n'entre pas en conflit avec les futures modifications de className():

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

}

}

Usage:

NSViewController.classNameCleaned
MyCustomClass.classNameCleaned
2
Darkwonder

Vous pouvez étendre NSObjectProtocol dans Swift 4 comme ceci:

import Foundation

extension NSObjectProtocol {

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

Cela rendra la variable calculée className disponible pour TOUTES les classes. L'utilisation de ceci dans une print() dans CalendarViewController imprimera "CalendarViewController" dans la console.

2
Siddharth Bhatt

Pour obtenir le nom de la classe sous forme de chaîne, déclarez votre classe comme suit

@objc(YourClassName) class YourClassName{}

Et obtenez le nom de la classe en utilisant la syntaxe suivante

NSStringFromClass(YourClassName)
1
Moin Uddin

Pour obtenir le nom d'une classe Swift à partir d'un objet, par exemple pour l'objet var: SomeClass (), utilisez

String(describing: type(of: object))

Pour obtenir le nom d'une classe Swift à partir d'un type de classe, par exemple SomeClass, utilisez:

String(describing: SomeClass.self)

Sortie:

"SomeClass"

1
Gurjit Singh

J'ai essayé type(of:...) dans Playground avec Swift 3. Voici mon résultat .   Ceci est la version de format de code.

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

Swift 3.0 (macOS 10.10 et supérieur), vous pouvez l'obtenir à partir de className

self.className.components(separatedBy: ".").last!
1
Thanh Vũ Trần

Si vous n'aimez pas le nom mutilé, vous pouvez dicter votre propre nom:

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

Cependant, il serait préférable à la longue d'apprendre à analyser le nom mutilé. Le format est standard et significatif et ne changera pas.

0
matt

Vous pouvez obtenir le nom de la classe en faisant quelque chose comme:

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

Je l'utilise dans Swift 2.2

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

Parfois, les autres solutions donneront un nom non utile en fonction de l'objet que vous essayez de regarder. Dans ce cas, vous pouvez obtenir le nom de la classe sous forme de chaîne en utilisant ce qui suit.

String(cString: object_getClassName(Any!))

Cliquez sur la fonction dans xcode pour voir des méthodes connexes assez utiles. ou vérifiez ici https://developer.Apple.com/reference/objectivec/objective_c_functions

0
bob