获取对象的类名为String,使用:

object_getClassName(myViewController)

返回如下内容:

_TtC5AppName22CalendarViewController

我正在寻找纯粹的版本:“CalendarViewController”。我如何得到一个清理类名字符串代替?

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


当前回答

更新到swift 5

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

例如,print(String(description: 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,我们就可以实例化该类的新对象:

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

也许这能帮助一些人!

其他回答

这类例子用于类var.不要包含bundle的名称。

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

要获得类名为String,请按以下方式声明类

@objc(YourClassName) class YourClassName{}

并使用以下语法获取类名

NSStringFromClass(YourClassName)

试着反映了()。类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;
}

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

在我的例子中,String(description: 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也可以。上面的函数也是故障安全的:)