【问题标题】:Get class name of object as string in Swift在 Swift 中获取对象的类名作为字符串
【发布时间】:2014-08-21 02:23:26
【问题描述】:

使用String 获取对象的类名:

object_getClassName(myViewController)

返回如下内容:

_TtC5AppName22CalendarViewController

我正在寻找 版本:"CalendarViewController"。我如何获得类名的 cleaned 字符串?

我发现了一些有关此问题的尝试,但没有实际答案。根本不可能吗?

【问题讨论】:

  • 或者……一个有效的解析器函数会是什么样子?
  • 如果您想检查一个对象是否属于某个类,请参阅this answer

标签: swift reflection typeof


【解决方案1】:

来自实例的字符串:

String(describing: self)

来自类型的字符串:

String(describing: YourType.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"

使用classstructenum 测试。

【讨论】:

  • 真的是这样吗?根据此初始化程序的 String 文档,当它不符合 Streamable、CustomStringConvertible 或 CustomDebugStringConvertible 时,“Swift 标准库会自动提供未指定的结果”。因此,虽然它现在可能会显示所需的值,但它会在未来继续这样做吗?
  • 在 Xcode 7.3.1 和 Swift 2.2 中使用它会产生以下结果,这并不理想。 ''let name = String(self) name String ""''
  • 在 Swift 3 中,正确的答案是调用 String(describing: MyViewController.self),如下面的几个答案所述。
  • 但它返回“MyViewController.TYPE”!
  • @RudolfAdamkovič 是的,那是真的。但是我不喜欢明确地写类名(如果我将类Foo 重命名为Foo2 但在其他地方创建一个类Foo,这可能会破坏代码)。在子类化的情况下,您需要使用type(of:)Type.self,具体取决于您的需要。可能type(of:) 更适合获取类名。
【解决方案2】:

更新到 SWIFT 5

我们可以通过String 初始化器使用实例变量获得类型名称的漂亮描述,并创建某个类的新对象

比如print(String(describing: type(of: object)))。其中object 可以是实例变量,如数组、字典、IntNSDate 等。

因为NSObject 是大多数Objective-C 类层次结构的根类,您可以尝试对NSObject 进行扩展以获取NSObject 的每个子类的类名。像这样:

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

或者你可以创建一个静态函数,其参数类型为Any(所有类型隐式遵循的协议)并将类名作为字符串返回。像这样:

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,我们就可以实例化该类的新对象

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

也许这对那里的人有帮助!

【讨论】:

  • classNameAsString 可以简化为className,因为“名称”暗示了它的类型。 theClassName与facebook的故事类似,最初命名为thefacebook。
  • "diccionary"...意大利语版的 Swift 词典合集 :]
  • 此解决方案为每个 classTag 添加项目名称,即ProjectTest.MyViewController。我如何摆脱这个项目名称?我只想要类名。
【解决方案3】:

斯威夫特 5

这是将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 {}
extension UIBarStyle: NameDescribable { }

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

// Out put:
UITabBarController
UINavigationController
Array<Int>
UIBarStyle

【讨论】:

  • 知道为什么我会从中得到 MyAppName.MyClassName 吗?我只对 MyClassName 感兴趣
  • @Andrew 你能提供更具体的情况吗?我在许多项目中都使用了这个扩展,所有项目都按预期响应。
  • 好吧,如果我从类本身调用它,它会返回 MyClassName,但是如果我将它包装在一个返回类名字符串的方法中并从另一个类调用它,它会返回 MyAppName.MyClassName。在四处搜索时,我看到所有类都是隐式命名空间的,所以如果你要在你的类之外调用它,你会得到 MyAppName.MyClassName 而不是 MyClassName。然后你必须依靠字符串操作来获取类名。
  • 非常有用的扩展
  • @Andrew,不确定当前的工作,但我记得,String(describing: type(of: self)) 曾经在某一时刻返回 ModuleName.ClassName。我基于 .字符并获取最后一个对象。
【解决方案4】:

斯威夫特 3.0

String(describing: MyViewController.self)

【讨论】:

  • 其实type(of: ) 有点矫枉过正,String(describing: MyViewController.self) 就足够了
  • 这会为我创建一个字符串,例如 ,这并不理想
  • 不是String.init(describing: MyViewController.self)吗?
  • @IulianOnofrei,你可以这样做,但 init 是不必要的。
  • 彻底去掉 AppName 前缀!谢谢
【解决方案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

【讨论】:

  • 他们在 Swift 3.0 中改变了它。现在你可以从type(of: self)获取字符串了
  • 更新到 Swift 3。
  • 我认为这是获取类型名称的最佳方式:类的任何实例,基于 NSObject、枚举、类型别名、函数、var 属性以及任何类型,甚至是常量。对于类型,您必须使用 .self。例如,typeName(Int.self)、typeName(true.self)。您也不必担心项目名称作为一部分(例如 AppProj.[typename])太好了!
【解决方案6】:

如果你输入了Foo,下面的代码会在 Swift 3 和 Swift 4 中给你"Foo"

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"

如果你只想要"Foo"type(of:) 部分是不必要的。

【讨论】:

    【解决方案7】:

    斯威夫特 5.2:

    String(describing: type(of: self))
    

    【讨论】:

      【解决方案8】:

      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
      

      【讨论】:

        【解决方案9】:

        斯威夫特 5.1

        您可以通过Self.self 获取类、结构、枚举、协议和 NSObject 名称。

        print("\(Self.self)")
        

        【讨论】:

        • 请注意,这可能会在模块名称之前添加(与String(describing: type(of: self))不同)
        • 是的,它看起来很简洁,但也预先添加了模块名称,我会说这是对问题的错误答案......
        • 这不适用于发布构建配置,它将以字符串形式返回 Self。有什么好的解释为什么会发生这种情况?
        【解决方案10】:

        从对象中获取 Swift 类的名称,例如对于 var 对象:SomeClass(),使用

        String(describing: type(of: object))
        

        从类类型中获取 Swift 类的名称,例如SomeClass,使用:

        String(describing: SomeClass.self)
        

        输出:

        “某类”

        【讨论】:

          【解决方案11】:

          你可以试试这个方法:

          self.classForCoder.description()
          

          【讨论】:

          • 这很好用,因为它还包含完整的“命名空间”(适当的目标成员资格前缀)。
          • 顺便说一句,我还发现使用“String(self)”,其中 self 是 MyViewController 的一个实例也可以工作......它提供了相同的信息...... Xcode 7.1。
          【解决方案12】:

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

          【讨论】:

            【解决方案13】:

            您可以像这样使用名为 _stdlib_getDemangledTypeName 的 Swift 标准库函数:

            let name = _stdlib_getDemangledTypeName(myViewController)
            

            【讨论】:

            • @NeilJaff “只是一个可选字符串”是什么意思?它返回一个字符串,它不是可选的。
            • 这种方法不会返回私有 API 中类的名称,仅供参考。它将返回第一个公共基类名称的名称。
            • 我收到消息:使用未解析的标识符 '_stdlib_getDemangledTypeName'
            【解决方案14】:

            也可以使用镜子:

            let vc = UIViewController()
            
            String(Mirror(reflecting: vc).subjectType)
            

            注意:此方法也可用于结构和枚举。有一个 displayStyle 可以指示结构的类型:

            Mirror(reflecting: vc).displayStyle
            

            返回是一个枚举,所以你可以:

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

            【讨论】:

            • 谢谢。这对我有用。唯一的事情(至少在 iOS 9 上)是 subjectType 以“.Type”结尾,所以我不得不从字符串中删除该部分。
            【解决方案15】:

            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 ?? ""
                }
            }
            

            【讨论】:

            • 当我尝试使用它时出现错误:无法将类型“ProjectName.MyViewController”(0x1020409e8)的值转换为“Swift.AnyObject.Type”(0x7fb96fc0dec0)。
            【解决方案16】:

            您可以像这样在 Swift 4 中扩展 NSObjectProtocol

            import Foundation
            
            extension NSObjectProtocol {
            
                var className: String {
                    return String(describing: Self.self)
                }
            }
            

            这将使计算变量className 可用于所有类。在CalendarViewController 中的print() 中使用它会在控制台中打印"CalendarViewController"

            【讨论】:

              【解决方案17】:

              要将类名作为 String 声明您的类,如下所示

              @objc(YourClassName) class YourClassName{}
              

              并使用以下语法获取类名

              NSStringFromClass(YourClassName)
              

              【讨论】:

                【解决方案18】:

                我一直在寻找这个答案一段时间。我使用 GKStateMachine 并且喜欢观察状态变化,并且想要一种简单的方法来查看类名。我不确定它只是 iOS 10 还是 Swift 2.3,但在那种环境中,以下内容正是我想要的:

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

                【讨论】:

                  【解决方案19】:

                  您可以通过以下方式获取类的名称:

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

                  【讨论】:

                    【解决方案20】:

                    在 Class self 或实例 dynamicType 上尝试 reflect().summary。在获取 dynamicType 之前解包可选项,否则 dynamicType 是 Optional 包装器。

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

                    【讨论】:

                      【解决方案21】:

                      上述解决方案对我不起作用。产生的主要是几个cmets中提到的问题:

                      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
                      

                      【讨论】:

                        【解决方案22】:

                        Swift 3.0(macOS 10.10 及更高版本),您可以从 className 获取它

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

                        【讨论】:

                        • 据我所知,Swift 类中没有内置的 className 属性。你是从什么子类化的吗?
                        • @shim className 在 Foundation 中声明,但它似乎只在 macOS 10.10 及更高版本上可用。我刚刚编辑了我的答案。
                        • 嗯,这很奇怪。为什么他们不将它包含在 iOS 中?另请注意,它是NSObject developer.apple.com/reference/objectivec/nsobject/… 上的实例方法
                        【解决方案23】:

                        我在 Playground 中使用 Swift 3 尝试了type(of:...)。这是我的结果。 这是代码格式版本。

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

                        【讨论】:

                        • 仅仅为了确定类名而实例化一个实例是很浪费的。
                        • @sethfri 我用它来确定动态类型。
                        • 我还是不明白为什么你需要创建一个实例只是为了获取类的类型信息
                        【解决方案24】:

                        斯威夫特 5

                         NSStringFromClass(CustomClass.self)
                        

                        【讨论】:

                          【解决方案25】:

                          类 var 的这种示例。不要包含捆绑包的名称。

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

                          【讨论】:

                            【解决方案26】:

                            如果您不喜欢乱码,您可以口述自己的名字:

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

                            但是,从长远来看,学习解析损坏的名称会更好。格式是标准且有意义的,不会改变。

                            【讨论】:

                            • 不,不是,对于我的类,它返回 (ExistentialMetatype) - 依赖未记录的运行时行为总是很危险的,尤其是对于 swift,因为它仍处于相对早期的阶段。
                            【解决方案27】:

                            有时其他解决方案会根据您要查看的对象给出无用的名称。在这种情况下,您可以使用以下命令将类名作为字符串获取。

                            String(cString: object_getClassName(Any!))
                            

                            ⌘点击xcode中的函数可以看到一些相关的方法,还是蛮有用的。或在这里查看https://developer.apple.com/reference/objectivec/objective_c_functions

                            【讨论】:

                              【解决方案28】:

                              Swift 5.1:-

                              你也可以使用泛型函数来获取对象的类名作为字符串

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

                              使用以下方法调用此函数:-

                              let name = GenericFunctions.className(ViewController.self)
                              

                              快乐编码:)

                              【讨论】:

                                【解决方案29】:

                                此解决方案适用于所有课程

                                Swift 5 解决方案:

                                extension NSObject {
                                  var className: String {
                                    return String(describing: type(of: self))
                                  }
                                
                                  class var className: String {
                                    return String(describing: self)
                                  }
                                }
                                

                                用法:

                                class TextFieldCell: UITableVIewCell {
                                }
                                
                                class LoginViewController: UIViewController {
                                  let cellClassName = TextFieldCell.className
                                }
                                

                                【讨论】:

                                  【解决方案30】:

                                  斯威夫特 5:

                                  方式一:

                                  print("Class: \(String(describing: self)), Function: \(#function), line: \(#line)")
                                  

                                  输出:

                                  Class: <Test.ViewController: 0x7ffaabc0a3d0>, Function: viewDidLoad(), line: 15
                                  

                                  方式二:

                                  print("Class: \(String(describing: type(of: self))), Function: \(#function), line: \(#line)")
                                  

                                  输出:

                                  Class: ViewController, Function: viewDidLoad(), line: 16
                                  

                                  【讨论】:

                                    猜你喜欢
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 2023-03-04
                                    • 2011-08-04
                                    • 2011-11-22
                                    • 2012-08-09
                                    相关资源
                                    最近更新 更多