在Swift中以字符串形式获取对象的类名


320

使用以下方法获取对象的类名String

object_getClassName(myViewController)

返回如下内容:

_TtC5AppName22CalendarViewController

我正在寻找版本:"CalendarViewController"。我该如何获取类名称的清理字符串?

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


或者……有效的解析器功能将是什么样子?
伯纳德2014年

如果您希望检查某个对象是否属于某个类,请参见此答案
含义-

Answers:


528

实例中的字符串:

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


4
真的是这样吗?根据此初始化程序的String文档,当它不符合Streamable,CustomStringConvertible或CustomDebugStringConvertible时,“ Swift标准库会自动提供未指定的结果”。因此,尽管现在可能显示所需的值,但将来会继续显示吗?
Tod Cunningham

3
在Xcode 7.3.1和Swift 2.2中使用它会产生以下结果,这是不理想的。”让名称=字符串(自己)名称字符串“ <MyAppName.MyClassName:0x ########>”“”
CodeBender

13
在Swift 3中,正确的答案是String(describing: MyViewController.self)按照下面几个答案中的说明进行呼叫。
2016年

10
但是它返回“ MyViewController.TYPE”!
orkenstein '16

3
@RudolfAdamkovič是的,那是真的。但是我不喜欢显式地编写类名(如果我将该类重命名FooFoo2却在Foo其他地方创建了一个类,那可能会破坏代码)。在子类化的情况下,您需要使用type(of:)Type.self根据需要选择适合的子类。可能type(of:)更适合于获取类名。
玛丽安·塞尔尼

182

更新为SWIFT 5

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

例如print(String(describing: type(of: object)))。在哪里object可以是实例变量,例如数组,字典,an Int,a 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,就可以实例化该类的新对象

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

也许这可以帮助某个人!


2
这应该是公认的答案。谢谢,我一直在寻找一种无需实例化即可打印类名的方法,并在此处找到了答案。感谢print(String(BaseAsyncTask))
Bruno Coelho

4
classNameAsString可以简化为className,因为该“名称”表示其类型。theClassName与facebook的故事相似,最初被称为thefacebook。
DawnSong

“字典” ...斯威夫特字典集的意大利语版:]
安德鲁·基尔纳

该解决方案在每个classTag的项目名称之前,即ProjectTest.MyViewController。我如何摆脱这个项目名称?我只想要班级名称。
Sazzad Hissain Khan,

132

斯威夫特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

12
任何想法为什么我得到MyAppName.MyClassName呢?我只对MyClassName感兴趣
Andrew

@Andrew您能否针对您的情况提供更具体的信息?我在许多项目中都使用了此扩展名,并且所有项目都按预期进行了响应。
nahung89 '16

1
好吧,如果我从类本身调用它,则返回MyClassName,但如果将其包装在返回类名称字符串的方法中,并从另一个类调用它,则返回MyAppName.MyClassName。在搜索时,我看到所有类都隐式命名空间,因此,如果要在类外部调用它,则会得到MyAppName.MyClassName而不是MyClassName。然后,您必须依靠字符串操作来获取类名。
安德鲁

1
非常有用的扩展名
HattoriHanzō18年

@Andrew,不确定当前的工作方式,但是我记得String(描述:type(of:self))曾经在某一时刻返回ModuleName.ClassName。我基于进行了拆分。字符并获取了最后一个对象。
BangOperator

31

斯威夫特3.0

String(describing: MyViewController.self)


2
实际上type(of: ),在那儿String(describing: MyViewController.self)已经足够大了,就足够了
Alexander Vasenin

2
这会为我创建一个字符串,例如<App_Name.ClassName:0x79e14450>,这并不理想
Doug Amos

是不是String.init(describing: MyViewController.self)
Iulian Onofrei '16

2
@IulianOnofrei,您可以这样做,但是init没有必要。
shim

完全摆脱了AppName前缀!谢谢
yuchen '17

28

我建议这样一种方法(非常迅速):

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


我认为这是获取类型名称的最佳方法:基于NSObject,枚举,类型别名,函数,var属性以及任何类型(甚至常量)的类的任何实例。对于类型,必须使用.self。例如,typeName(Int.self),typeName(true.self)。您也不必担心项目名称作为一部分(例如AppProj。[typename])。
David.Chu.ca,

23

如果您具有type Foo,则以下代码将"Foo"在Swift 3和Swift 4中为您提供:

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

此处大多数答案的问题在于,"Foo.Type"当您没有任何类型的实例时,当您真正想要的是just时,它们将为您提供结果字符串"Foo"。如"Foo.Type"其他答案中所述,以下内容为您提供了。

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

type(of:)如果您只是想要,则不需要该部分"Foo"


21

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

14

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

String(describing: type(of: object))

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

String(describing: SomeClass.self)

输出:

“ SomeClass”


13

您可以这样尝试:

self.classForCoder.description()

由于它包括完整的“命名空间”(适当的目标成员资格前缀),因此效果很好。
BonanzaDriver

顺便说一句,我还发现使用“ String(self)”,其中self也是MyViewController的一个实例,它也提供相同的信息……Xcode 7.1。
BonanzaDriver

12

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

let name = _stdlib_getDemangledTypeName(myViewController)

@NeilJaff“仅是可选字符串”是什么意思?它返回一个字符串,并且不是可选的。
Jernej Strasner 2015年

3
这种方法不会返回专用API FYI中的类的名称。它将返回第一个公共基类名称的名称。
Tylerc230

我收到消息:使用未解决的标识符'_stdlib_getDemangledTypeName'–
Adobels

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

8

一个人也可以使用镜子:

let vc = UIViewController()

String(Mirror(reflecting: vc).subjectType)

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

Mirror(reflecting: vc).displayStyle

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

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

谢谢。那对我有用。唯一的事情(至少在iOS 9上是如此)是subjectType以“ .Type”结尾,因此我不得不从字符串中删除该部分。
Nikolay Suvandzhiev


7

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)。
tylerSF

6

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

import Foundation

extension NSObjectProtocol {

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

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


4

我一直在寻找这个答案一段时间。我使用GKStateMachine并喜欢观察状态变化,并且想要一种简单的方法来仅查看类名称。我不确定是iOS 10还是Swift 2.3,但是在这种环境下,以下功能确实可以满足我的要求:

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

// Output:    
// Class Name: GKState


3

要获取类名称为String,请声明您的类,如下所示

@objc(YourClassName) class YourClassName{}

并使用以下语法获取类名称

NSStringFromClass(YourClassName)

3

尝试reflect().summaryClass自身或实例dynamicType。在获取dynamicType之前,请先取消包装Optionals,否则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;
}

3

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

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

2

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

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

1
据我所知,Swift类中没有内置的className属性。你是从某种事物中继承出来的吗?
shim

@shim className在Foundation中声明,但似乎仅在macOS 10.10及更高版本上可用。我刚刚编辑了答案。
ThanhVũTrần16年

1
嗯,这很奇怪。他们为什么不将其包含在iOS中?还要注意,这是NSObject developer.apple.com/reference/objectivec/nsobject/…
shim

2

斯威夫特5

 NSStringFromClass(CustomClass.self)


1

如果您不喜欢名字乱七八糟的名字,可以指定自己的名字:

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

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


不,不是这样,对于我的类,它返回(ExistentialMetatype)-依赖未记录的运行时行为总是很危险的,尤其是对于swift来说,因为它仍处于相对较早的阶段。
superarts.org 2014年


1

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

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

实例化实例只是为了确定类名是浪费的。
sethfri

@sethfri我用它来确定动态类型。
Lumialxk '17

我仍然不明白为什么您只需要创建一个实例来获取类的类型信息
sethfri

1

Swift 5您可以使用String获得class_name作为字符串(描述:Self.self)

print(String(describing: Self.self))


0

斯威夫特5.1:-

您还可以使用泛型函数将对象的类名称作为字符串获取

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

}

使用以下命令调用此函数:

let name = GenericFunctions.className(ViewController.self)

快乐编码:)


-1

在我的情况下,String(describing: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也应该起作用。上面的功能也是故障安全的:)


那是因为您传递的是类名的实例而不是类,因此在上面的示例中,您应该传递ExampleViewController.self
Luke
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.