Получение имени класса в виде строки с помощью
object_getClassName(myViewController)
возвращает что-то вроде этого
_TtC5AppName22CalendarViewController
Я ищу чистую версию: "CalendarViewController". Как получить очищенную строку имени класса?
Я нашел несколько попыток вопросов об этом, но не фактический ответ. Разве это невозможно?
Строка из экземпляра:
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
.
ОБНОВЛЕНО к SWIFT 4.2
Мы можем получить красивые описания имен типов, используя переменную экземпляра через инициализатор 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("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
}
}
Кроме того, как только мы можем получить имя класса как 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)
Может быть, это помогает кому-то там!
classNameAsString
можно упростить до className
, поскольку это «имя» подразумевает его тип. theClassName
является theClassName
истории Facebook, которая изначально называется facebook.
Вот расширение для получения 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)
Swift 3.0
String(describing: MyViewController.self)
type(of: )
String(describing: MyViewController.self)
является излишним, String(describing: MyViewController.self)
будет достаточно
Я предлагаю такой подход (очень 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
type(of: self)
Если у вас есть тип 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"
.
В 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
Вы можете использовать стандартную библиотечную функцию Swift под названием _stdlib_getDemangledTypeName
следующим образом:
let name = _stdlib_getDemangledTypeName(myViewController)
Чтобы получить имя типа как строку в 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()))"
Вы можете попробовать так:
self.classForCoder.description()
Можно также использовать зеркала:
let vc = UIViewController()
String(Mirror(reflecting: vc).subjectType)
NB: этот метод также можно использовать для Structs и Enums. Существует displayStyle, который дает представление о том, какой тип структуры:
Mirror(reflecting: vc).displayStyle
Возврат - это перечисление, чтобы вы могли:
Mirror(reflecting: vc).displayStyle == .Class
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 ?? ""
}
}
Я искал этот ответ и продолжал какое-то время. Я использую GKStateMachine и люблю наблюдать изменения состояния и хотел бы просто увидеть просто имя класса. Я не уверен, что это только iOS 10 или Swift 2.3, но в этой среде следующее делает именно то, что я хочу:
let state:GKState?
print("Class Name: \(String(state.classForCoder)")
// Output:
// Class Name: GKState
Вышеупомянутые решения не сработали для меня. Произведенные в основном проблемы упоминаются в нескольких комментариях:
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
Попробуйте 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;
}
Вы можете расширить NSObjectProtocol
в Swift 4 следующим образом:
import Foundation
extension NSObjectProtocol {
var className: String {
return String(describing: Self.self)
}
}
Это сделает рассчитанную переменную className
доступной для ВСЕХ классов. Используя это внутри print()
в CalendarViewController
, напечатает "CalendarViewController"
в консоли.
Я пробовал type(of:...)
в Playground с Swift 3. Это мой результат.
Это версия формата кода.
print(String(describing: type(of: UIButton.self)))
print(String(describing: type(of: UIButton())))
UIButton.Type
UIButton
Swift 3.0 (macOS 10.10 и новее), вы можете получить его из классаName
self.className.components(separatedBy: ".").last!
Чтобы получить имя класса как String, объявите свой класс следующим образом
@objc(YourClassName) class YourClassName{}
И получите имя класса, используя следующий синтаксис
NSStringFromClass(YourClassName)
Иногда другие решения дают не полезное имя в зависимости от того, на какой объект вы пытаетесь посмотреть. В этом случае вы можете получить имя класса в виде строки, используя следующее.
String(cString: object_getClassName(Any!))
⌘ щелкните функцию в xcode, чтобы увидеть некоторые связанные методы, которые довольно полезны. или здесь https://developer.apple.com/reference/objectivec/objective_c_functions
Я использую его в Swift 2.2
guard let currentController = UIApplication.topViewController() else { return }
currentController.classForCoder.description().componentsSeparatedByString(".").last!
Если вам не нравится измененное имя, вы можете указать свое имя:
@objc(CalendarViewController) class CalendarViewController : UIViewController {
// ...
}
Однако в конечном итоге было бы лучше научиться разбирать искаженное имя. Формат является стандартным и значимым и не изменится.
В моем случае 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 также должно работать. Функция выше также является отказоустойчивой:)