Получить имя класса объекта как строку в Swift

Получение имени класса в виде строки с помощью

object_getClassName(myViewController)

возвращает что-то вроде этого

_TtC5AppName22CalendarViewController

Я ищу чистую версию: "CalendarViewController". Как получить очищенную строку имени класса?

Я нашел несколько попыток вопросов об этом, но не фактический ответ. Разве это невозможно?

Ответ 1

Строка из экземпляра:

String(describing: YourType.self)

Строка от типа:

String(describing: self)

Пример:

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"

Протестировано с помощью class, struct и enum.

Ответ 2

ОБНОВЛЕНО до SWIFT 5

Мы можем получить красивые описания имен типов , используя переменную экземпляра через инициализатор String и создавать новые объекты определенного класса

Как, например, print(String(describing: type(of: object))). Где object может быть переменной экземпляра, таким как массив, словарь, Int, NSDate и т.д.

Поскольку NSObject является корневым классом большинства иерархий классов Objective-C, вы можете попытаться создать расширение для NSObject, чтобы получить имя класса каждого подкласса в NSObject. Вот так:

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

Или вы можете создать статическую функцию, параметр которой имеет тип Any (протокол, которому неявно соответствуют все типы), и возвращает имя класса в виде String. Вот так:

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

Теперь вы можете сделать что-то вроде этого:

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

    }
}

Кроме того, как только мы можем получить имя класса в виде String, мы можем создавать новые объекты этого класса:

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

Может быть, это помогает кому-то там!

Ответ 3

Swift 4.2

Вот расширение для получения typeName в качестве переменной (как для типа значения, так и для ссылочного типа).

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

Как пользоваться:

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

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

Ответ 4

Swift 3.0

String(describing: MyViewController.self)

Ответ 5

Я предлагаю такой подход (очень 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)"
}

Он не использует ни интроспекции, ни ручного демонтажа (без магии!).


Вот демо:

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

Ответ 6

Если у вас есть тип Foo, следующий код даст вам "Foo" в Swift 3 и Swift 4:

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

Проблема с большинством ответов здесь заключается в том, что они дают вам "Foo.Type" в качестве результирующей строки, когда у вас нет экземпляра типа, когда то, что вы действительно хотите, это просто "Foo". Ниже приведено "Foo.Type", как упоминалось в нескольких других ответах.

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

Часть type(of:) не нужна, если вы просто хотите "Foo".

Ответ 7

В Swift 4.1 и сейчас 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)

Если у вас нет экземпляра вокруг:

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

Если у вас есть экземпляр вокруг:

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

Ответ 8

Вы можете использовать стандартную библиотечную функцию Swift под названием _stdlib_getDemangledTypeName следующим образом:

let name = _stdlib_getDemangledTypeName(myViewController)

Ответ 9

Вы можете попробовать так:

self.classForCoder.description()

Ответ 10

Чтобы получить имя типа как строку в Swift 4 (я не проверял более ранние версии), просто используйте интерполяцию строк:

"\(type(of: myViewController))"

Вы можете использовать .self для самого типа и функцию type(of:_) для экземпляра:

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

Ответ 11

Можно также использовать зеркала:

let vc = UIViewController()

String(Mirror(reflecting: vc).subjectType)

NB: этот метод также можно использовать для Structs и Enums. Существует displayStyle, который дает представление о том, какой тип структуры:

Mirror(reflecting: vc).displayStyle

Возврат - это перечисление, чтобы вы могли:

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

Ответ 12

Swift 3.0: Вы можете создать расширение, подобное этому. Оно возвращает имя класса без имени проекта

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

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

Ответ 13

Чтобы получить имя класса Swift из объекта, например, для объекта var: SomeClass(), используйте

String(describing: type(of: object))

Чтобы получить имя класса Swift из типа класса, например SomeClass, используйте:

String(describing: SomeClass.self)

Выход:

"SomeClass"

Ответ 14

Я искал этот ответ и продолжал какое-то время. Я использую GKStateMachine и люблю наблюдать изменения состояния и хотел бы просто увидеть просто имя класса. Я не уверен, что это только iOS 10 или Swift 2.3, но в этой среде следующее делает именно то, что я хочу:

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

// Output:    
// Class Name: GKState

Ответ 15

Вы можете расширить NSObjectProtocol в Swift 4 следующим образом:

import Foundation

extension NSObjectProtocol {

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

Это сделает рассчитанную переменную className доступной для ВСЕХ классов. Используя это внутри print() в CalendarViewController, напечатает "CalendarViewController" в консоли.

Ответ 16

Попробуйте reflect().summary в классе self или экземпляре dynamicType. Разверните опции, прежде чем получать dynamicType, иначе dynamicType будет необязательной оберткой.

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;

Сводка - это путь класса с описанными общими типами. Чтобы получить простое имя класса из резюме, попробуйте этот метод.

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

Ответ 17

Вышеупомянутые решения не сработали для меня. Произведенные в основном проблемы упоминаются в нескольких комментариях:

MyAppName.ClassName

или

MyFrameWorkName.ClassName

Эти решения работали на XCode 9, Swift 3.0:

Я назвал его classNameCleaned, поэтому его легче получить доступ и не конфликтует с будущими изменениями 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()
    }

}

}

Использование:

NSViewController.classNameCleaned
MyCustomClass.classNameCleaned

Ответ 18

Чтобы получить имя класса как String, объявите свой класс следующим образом

@objc(YourClassName) class YourClassName{}

И получите имя класса, используя следующий синтаксис

NSStringFromClass(YourClassName)

Ответ 19

Swift 3.0 (macOS 10.10 и новее), вы можете получить его из классаName

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

Ответ 20

Я пробовал type(of:...) в Playground с Swift 3. Это мой результат. Это версия формата кода.

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

Ответ 21

Вы можете получить имя класса, сделав что-то вроде:

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

Ответ 22

Если вам не нравится измененное имя, вы можете указать свое имя:

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

Однако в конечном итоге было бы лучше научиться разбирать искаженное имя. Формат является стандартным и значимым и не изменится.

Ответ 23

Я использую его в Swift 2.2

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

Ответ 24

Иногда другие решения дают не полезное имя в зависимости от того, на какой объект вы пытаетесь посмотреть. В этом случае вы можете получить имя класса в виде строки, используя следующее.

String(cString: object_getClassName(Any!))

⌘ щелкните функцию в xcode, чтобы увидеть некоторые связанные методы, которые довольно полезны. или здесь https://developer.apple.com/reference/objectivec/objective_c_functions

Ответ 25

Swift 5

 NSStringFromClass(CustomClass.self)

Ответ 26

Этот вид примера для класса вар. Не включайте название пакета.

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

Ответ 27

В моем случае String (описывая: self) вернула что-то вроде:

< My_project.ExampleViewController: 0x10b2bb2b0 >

Но я бы хотел иметь что-то вроде getSimpleName на Android.

Итак, я создал небольшое расширение:

extension UIViewController {

    func getSimpleClassName() -> String {
        let describing = String(describing: self)
        if let dotIndex = describing.index(of: "."), let commaIndex = describing.index(of: ":") {
            let afterDotIndex = describing.index(after: dotIndex)
            if(afterDotIndex < commaIndex) {
                return String(describing[afterDotIndex ..< commaIndex])
            }
        }
        return describing
    }

}

И теперь он возвращает:

ExampleViewController

Расширение NSObject вместо UIViewController также должно работать. Функция выше также является отказоустойчивой:)