首页 文章

在Swift中获取对象的类名作为字符串

提问于
浏览
228

使用获取对象的类名作为字符串

object_getClassName(myViewController)

返回这样的东西

_TtC5AppName22CalendarViewController

我正在寻找纯粹的版本:"CalendarViewController" . 如何获得清理的类名字符串?

我发现了一些有关此问题的尝试但不是真正的答案 . 这不可能吗?

23 回答

  • 0

    如果您有类型 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

    来自实例的字符串:

    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"
    

    使用 classstructenum 进行了测试 .

  • -1

    UPDATED TO SWIFT 4.2

    我们可以通过 String 初始化程序和 create new objects of a certain class 获得类型名称 using the instance variable 的漂亮描述

    比如,例如 print(String(describing: type(of: object))) . object 可以是 instance variable 数组,字典, IntNSDate 等 .

    因为 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, we can instanciate new objects of that class

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

    也许这有助于那里的人!

  • 410

    Swift 3.1

    这是将 className 作为变量(对于实例或类对象)的扩展 .

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

    如何使用:

    print(self.className)
    print(ViewController.className)
    
  • 19

    Swift 3.0

    String(describing: MyViewController.self)

  • 13

    我建议这样的方法(非常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
    
  • 1

    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
    
  • 1

    您可以使用名为 _stdlib_getDemangledTypeName 的Swift标准库函数,如下所示:

    let name = _stdlib_getDemangledTypeName(myViewController)
    
  • 29

    要在 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()))"
    
  • 96

    还可以使用镜子:

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

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

    Mirror(reflecting: vc).displayStyle
    

    返回是枚举,因此您可以:

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

    你可以这样试试:

    self.classForCoder.description()
    
  • 6

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

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

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

    在Class self或instance 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;
    }
    
  • 0

    上述解决方案对我不起作用 . 主要产生的问题在几条评论中提到:

    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
    
  • 27

    要将类名称作为String,请将您的类声明如下

    @objc(YourClassName) class YourClassName{}
    

    并使用以下语法获取类名

    NSStringFromClass(YourClassName)
    
  • 107

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

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

    我在Playground中使用Swift 3尝试了 type(of:...) . 这是我的结果 .

    这是代码格式版本 .

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

    您可以在Swift 4中扩展 NSObjectProtocol ,如下所示:

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

    这将使所有类都可以使用计算变量 className . 在 print() 中使用 print() 将在控制台中打印 "CalendarViewController" .

  • 0

    如果您不喜欢受损的名称,您可以指定自己的名字:

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

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

  • 1

    我在Swift 2.2中使用它

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

    有时,其他解决方案会根据您要查看的对象提供无用的名称 . 在这种情况下,您可以将类名称作为使用以下字符串 .

    String(cString: object_getClassName(Any!))
    

    ⌘单击xcode中的函数以查看一些非常有用的相关方法 . 或者点击这里https://developer.apple.com/reference/objectivec/objective_c_functions

  • 2

    在我的情况下,String(描述:self)返回类似于:

    <My_project.ExampleViewController:0x10b2bb2b0>

    但是我想在Android上使用 getSimpleName 之类的东西 .

    所以我创建了一个小扩展:

    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也应该可以工作 . 上面的功能也是故障安全的:)

相关问题