我如何在Swift中打印变量的类型或类?

是否有一种方法可以在swift中打印变量的运行时类型?例如:

var now = NSDate()
var soon = now.dateByAddingTimeInterval(5.0)


println("\(now.dynamicType)")
// Prints "(Metatype)"


println("\(now.dynamicType.description()")
// Prints "__NSDate" since objective-c Class objects have a "description" selector


println("\(soon.dynamicType.description()")
// Compile-time error since ImplicitlyUnwrappedOptional<NSDate> has no "description" method

在上面的例子中,我正在寻找一种方法来显示变量“soon”是ImplicitlyUnwrappedOptional<NSDate>类型,或者至少是NSDate!类型。

253397 次浏览

您仍然可以通过className(返回String)访问该类。

实际上有几种方法来获取类,例如classForArchiverclassForCoderclassForKeyedArchiver(都返回AnyClass!)。

你不能得到一个基元的类型(基元是一个类)。

例子:

var ivar = [:]
ivar.className // __NSDictionaryI


var i = 1
i.className // error: 'Int' does not have a member named 'className'

如果你想获取原语的类型,你必须使用bridgeToObjectiveC()。例子:

var i = 1
i.bridgeToObjectiveC().className // __NSCFNumber

编辑:在Swift 1.2 (Xcode 6.3)中引入了一个新的toString函数< A href="http://swiftdoc.org/func/toString/" rel="noreferrer">

你现在可以使用.self打印任意类型的需求类型,使用.dynamicType打印任意实例:

struct Box<T> {}


toString("foo".dynamicType)            // Swift.String
toString([1, 23, 456].dynamicType)     // Swift.Array<Swift.Int>
toString((7 as NSNumber).dynamicType)  // __NSCFNumber


toString((Bool?).self)                 // Swift.Optional<Swift.Bool>
toString(Box<SinkOf<Character>>.self)  // __lldb_expr_1.Box<Swift.SinkOf<Swift.Character>>
toString(NSStream.self)                // NSStream

尝试调用YourClass.selfyourObject.dynamicType

参考:https://devforums.apple.com/thread/227425

似乎没有通用的方法来打印任意值的类型的类型名。正如其他人所注意到的,对于实例,你可以打印value.className,但对于基本值,在运行时,类型信息消失了。

例如,似乎没有办法输入:1.something()并为something的任何值获取Int。(你可以,正如另一个答案所建议的,使用i.bridgeToObjectiveC().className来给你一个提示,但__NSCFNumber实际上是的类型i——当它越过Objective-C函数调用的边界时,它将被转换为这种类型。)

我很乐意被证明是错的,但看起来类型检查都是在编译时完成的,而且像c++(禁用RTTI)一样,很多类型信息在运行时就消失了。

当使用Cocoa(不是CocoaTouch)时,你可以为NSObject的子类对象使用className属性。

println(now.className)

这个属性对于普通的Swift对象是不可用的,它不是NSObject的子类(事实上,在Swift中没有根id或对象类型)。

class Person {
var name: String?
}


var p = Person()
println(person.className) // <- Compiler error

在CocoaTouch中,此时没有办法获得给定变量类型的字符串描述。类似的功能在Cocoa或CocoaTouch中也不存在。

Swift REPL能够打印出值的摘要,包括它的类型,所以这种内省方式可能在未来通过API实现。

编辑: dump(object)似乎可以做到这一点。

这就是你要找的吗?

println("\(object_getClassName(now))");

它输出__NSDate

更新:请注意,这似乎不再工作Beta05

可以使用reflect来获取对象的信息 例如对象类的名称:

var classname = reflect(now).summary

在lldb beta 5中,你可以通过以下命令查看对象的类:

fr v -d r shipDate

输出如下:

(DBSalesOrderShipDate_DBSalesOrderShipDate_ *) shipDate = 0x7f859940

展开的命令是这样的:

Frame Variable(输出帧变量)

需要知道的一点是,使用“Frame Variable”来输出变量值可以确保不执行任何代码。

2016年9月更新

Swift 3.0:使用type(of:),例如type(of: someThing)(因为dynamicType关键字已被删除)

2015年10月更新:

我将下面的示例更新为新的Swift 2.0语法(例如,printlnprint取代,toString()现在是String())。

来自Xcode 6.3发布说明:

@nschum在评论中指出Xcode 6.3发布说明显示了另一种方式:

类型值现在打印为完整的需求类型名称时使用

import Foundation


class PureSwiftClass { }


var myvar0 = NSString() // Objective-C class
var myvar1 = PureSwiftClass()
var myvar2 = 42
var myvar3 = "Hans"


print( "String(myvar0.dynamicType) -> \(myvar0.dynamicType)")
print( "String(myvar1.dynamicType) -> \(myvar1.dynamicType)")
print( "String(myvar2.dynamicType) -> \(myvar2.dynamicType)")
print( "String(myvar3.dynamicType) -> \(myvar3.dynamicType)")


print( "String(Int.self)           -> \(Int.self)")
print( "String((Int?).self         -> \((Int?).self)")
print( "String(NSString.self)      -> \(NSString.self)")
print( "String(Array<String>.self) -> \(Array<String>.self)")

输出:

String(myvar0.dynamicType) -> __NSCFConstantString
String(myvar1.dynamicType) -> PureSwiftClass
String(myvar2.dynamicType) -> Int
String(myvar3.dynamicType) -> String
String(Int.self)           -> Int
String((Int?).self         -> Optional<Int>
String(NSString.self)      -> NSString
String(Array<String>.self) -> Array<String>

Xcode 6.3更新:

你可以使用_stdlib_getDemangledTypeName():

print( "TypeName0 = \(_stdlib_getDemangledTypeName(myvar0))")
print( "TypeName1 = \(_stdlib_getDemangledTypeName(myvar1))")
print( "TypeName2 = \(_stdlib_getDemangledTypeName(myvar2))")
print( "TypeName3 = \(_stdlib_getDemangledTypeName(myvar3))")

并将其作为输出:

TypeName0 = NSString
TypeName1 = __lldb_expr_26.PureSwiftClass
TypeName2 = Swift.Int
TypeName3 = Swift.String

最初的回答:

在Xcode 6.3之前,_stdlib_getTypeName获取变量的类型名。伊万·斯维克的博客帮助解码这些字符串:

例如:_TtSi代表Swift内部的Int类型。

Mike Ash有一篇很棒的博客文章涉及了同样的主题

我的运气是:

let className = NSStringFromClass(obj.dynamicType)

我目前的Xcode版本是6.0 (6A280e)。

import Foundation


class Person { var name: String; init(name: String) { self.name = name }}
class Patient: Person {}
class Doctor: Person {}


var variables:[Any] = [
5,
7.5,
true,
"maple",
Person(name:"Sarah"),
Patient(name:"Pat"),
Doctor(name:"Sandy")
]


for variable in variables {
let typeLongName = _stdlib_getDemangledTypeName(variable)
let tokens = split(typeLongName, { $0 == "." })
if let typeName = tokens.last {
println("Variable \(variable) is of Type \(typeName).")
}
}

输出:

Variable 5 is of Type Int.
Variable 7.5 is of Type Double.
Variable true is of Type Bool.
Variable maple is of Type String.
Variable Swift001.Person is of Type Person.
Variable Swift001.Patient is of Type Patient.
Variable Swift001.Doctor is of Type Doctor.

我在这里尝试了一些其他的答案,但milage似乎很清楚下面的对象是什么。

然而,我确实发现了一种方法,你可以通过以下方式获得对象的object - c类名:

now?.superclass as AnyObject! //replace now with the object you are trying to get the class name for

下面是一个如何使用它的例子:

let now = NSDate()
println("what is this = \(now?.superclass as AnyObject!)")

在本例中,它将在控制台中打印NSDate。

我找到了一个自开发类的解决方案(或这样你可以访问)。

将以下计算属性放在对象类定义中:

var className: String? {
return __FILE__.lastPathComponent.stringByDeletingPathExtension
}

现在你可以像这样简单地调用对象的类名:

myObject.className

请注意,这将只有当你的类定义是在一个名为完全的文件中创建的,就像你想要的类名一样。

因为这是一般,所以情况是上面的答案应该适用于大多数情况。但在一些特殊情况下,你可能需要想出一个不同的解决方案。


如果你需要类名在类(文件)本身中,你可以简单地使用这一行:

let className = __FILE__.lastPathComponent.stringByDeletingPathExtension

也许这个方法能帮助到一些人。

根据上面Klass和Kevin Ballard给出的答案和评论,我认为:

println(_stdlib_getDemangledTypeName(now).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon?).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon!).componentsSeparatedByString(".").last!)


println(_stdlib_getDemangledTypeName(myvar0).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar1).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar2).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar3).componentsSeparatedByString(".").last!)

这将打印出:

"NSDate"
"ImplicitlyUnwrappedOptional"
"Optional"
"NSDate"


"NSString"
"PureSwiftClass"
"Int"
"Double"
let i: Int = 20




func getTypeName(v: Any) -> String {
let fullName = _stdlib_demangleName(_stdlib_getTypeName(i))
if let range = fullName.rangeOfString(".") {
return fullName.substringFromIndex(range.endIndex)
}
return fullName
}


println("Var type is \(getTypeName(i)) = \(i)")
我找到了这个解决方案,希望对其他人也有用。 我创建了一个类方法来访问该值。请记住,这只适用于NSObject子类。但至少是一个干净整洁的解决方案
class var className: String!{
let classString : String = NSStringFromClass(self.classForCoder())
return classString.componentsSeparatedByString(".").last;
}

从Xcode 6.3和Swift 1.2开始,你可以简单地将类型值转换为完整的String

toString(Int)                   // "Swift.Int"
toString(Int.Type)              // "Swift.Int.Type"
toString((10).dynamicType)      // "Swift.Int"
println(Bool.self)              // "Swift.Bool"
println([UTF8].self)            // "Swift.Array<Swift.UTF8>"
println((Int, String).self)     // "(Swift.Int, Swift.String)"
println((String?()).dynamicType)// "Swift.Optional<Swift.String>"
println(NSDate)                 // "NSDate"
println(NSDate.Type)            // "NSDate.Type"
println(WKWebView)              // "WKWebView"
toString(MyClass)               // "[Module Name].MyClass"
toString(MyClass().dynamicType) // "[Module Name].MyClass"

斯威夫特5

在最新版本的Swift 3中,我们可以通过String初始化器获得对类型名称的详细描述。例如,print(String(describing: type(of: object)))。其中object 可以是一个实例变量,如数组、字典、IntNSDate、自定义类的实例等。

这是我的完整答案:在Swift中获取对象的类名为字符串

这个问题是寻找一种方法来获取对象的类名作为字符串,但我也提出了另一种方法来获取变量的类名,而不是NSObject的子类。下面就是:

class Utility{
class func classNameAsString(obj: Any) -> String {
//prints more readable results for dictionaries, arrays, Int, etc
return String(describing: type(of: obj))
}
}

我创建了一个静态函数,它接受类型为Any的对象作为参数,并返回其类名String:)。

我用一些变量测试了这个函数,比如:

    let diccionary: [String: CGFloat] = [:]
let array: [Int] = []
let numInt = 9
let numFloat: CGFloat = 3.0
let numDouble: Double = 1.0
let classOne = ClassOne()
let classTwo: ClassTwo? = ClassTwo()
let now = NSDate()
let lbl = UILabel()

结果是:

  • Dictionary的类型为Dictionary
  • array的类型是array
  • numInt是Int类型
  • numFloat类型为CGFloat
  • numDouble是Double类型
  • classOne的类型为:classOne
  • classTwo的类型为:classTwo
  • now的类型是:日期
  • lbl类型为:UILabel

在最新的XCode 6.3和Swift 1.2中,这是我发现的唯一方法:

if view.classForCoder.description() == "UISegment" {
...
}

不完全是你想要的,但你也可以检查Swift类型的变量类型,像这样:

let object: AnyObject = 1


if object is Int {
}
else if object is String {
}

为例。

斯威夫特3.0

let string = "Hello"
let stringArray = ["one", "two"]
let dictionary = ["key": 2]


print(type(of: string)) // "String"


// Get type name as a string
String(describing: type(of: string)) // "String"
String(describing: type(of: stringArray)) // "Array<String>"
String(describing: type(of: dictionary)) // "Dictionary<String, Int>"


// Get full type as a string
String(reflecting: type(of: string)) // "Swift.String"
String(reflecting: type(of: stringArray)) // "Swift.Array<Swift.String>"
String(reflecting: type(of: dictionary)) // "Swift.Dictionary<Swift.String, Swift.Int>"

这里的许多答案都不支持最新的Swift(本文撰写时为Xcode 7.1.1)。

当前获取信息的方法是创建Mirror并询问它。对于类名,它很简单:

let mirror = Mirror(reflecting: instanceToInspect)
let classname:String = mirror.description

关于对象的其他信息也可以从Mirror中检索。详见http://swiftdoc.org/v2.1/type/Mirror/

Xcode 7.3.1, Swift 2.2:

String(instanceToPrint.self).componentsSeparatedByString(".").last

在Swift 3.0中,你可以使用type(of:),因为dynamicType关键字已被删除。

Xcode 8 Swift 3.0使用类型:

let className = "\(type(of: instance))"

在Xcode 8, Swift 3.0

步骤:

1. 获取类型:

选项1:

let type : Type = MyClass.self  //Determines Type from Class

选项2:

let type : Type = type(of:self) //Determines Type from self

2. 转换类型为字符串:

let string : String = "\(type)" //String

Swift 3.0, Xcode 8

使用下面的代码,您可以向实例请求其类。你也可以比较两个实例,是否具有相同的类。

// CREATE pure SWIFT class
class MySwiftClass {
var someString : String = "default"
var someInt    : Int = 5
}


// CREATE instances
let firstInstance = MySwiftClass()
let secondInstance = MySwiftClass()
secondInstance.someString = "Donald"
secondInstance.someInt = 24


// INSPECT instances
if type(of: firstInstance) === MySwiftClass.self {
print("SUCCESS with ===")
} else {
print("PROBLEM with ===")
}


if type(of: firstInstance) == MySwiftClass.self {
print("SUCCESS with ==")
} else {
print("PROBLEM with ==")
}


// COMPARE CLASS OF TWO INSTANCES
if type(of: firstInstance) === type(of: secondInstance) {
print("instances have equal class")
} else {
print("instances have NOT equal class")
}

这在检查一个对象是否是类的类型时也很方便:

if someObject is SomeClass {
//someObject is a type of SomeClass
}

请看看下面的代码片段,让我知道你是否在寻找下面这样的东西。

var now = NSDate()
var soon = now.addingTimeInterval(5.0)


var nowDataType = Mirror(reflecting: now)
print("Now is of type: \(nowDataType.subjectType)")


var soonDataType = Mirror(reflecting: soon)
print("Soon is of type: \(soonDataType.subjectType)")

影响String(describing: type(of: self))返回的类名的另一个重要方面是访问控制

考虑下面的例子,基于斯威夫特3.1.1Xcode 8.3.3(2017年7月)

func printClassNames() {


let className1 = SystemCall<String>().getClassName()
print(className1) // prints: "SystemCall<String>"


let className2 = DemoSystemCall().getClassName()
print(className2) // prints: "DemoSystemCall"


// private class example
let className3 = PrivateDemoSystemCall().getClassName()
print(className3) // prints: "(PrivateDemoSystemCall in _0FC31E1D2F85930208C245DE32035247)"


// fileprivate class example
let className4 = FileprivateDemoSystemCall().getClassName()
print(className4) // prints: "(FileprivateDemoSystemCall in _0FC31E1D2F85930208C245DE32035247)"
}


class SystemCall<T> {
func getClassName() -> String {
return String(describing: type(of: self))
}
}


class DemoSystemCall: SystemCall<String> { }


private class PrivateDemoSystemCall: SystemCall<String> { }


fileprivate class FileprivateDemoSystemCall: SystemCall<String> { }

如你所见,本例中的所有类都有不同级别的访问控制,这些访问控制会影响它们的String表示。如果类有privatefileprivate访问控制级别,Swift似乎会附加某种与类的“嵌套”类相关的标识符。

PrivateDemoSystemCallFileprivateDemoSystemCall的结果都是附加了相同的标识符,因为它们都嵌套在同一个父类中。

我还没有找到一种方法来摆脱,除了一些hack替换或正则表达式函数。

这只是我的个人意见。

上面的答案没有使用type(of:的新方法的工作示例。所以为了帮助像我这样的新手,这里有一个工作示例,主要取自Apple的文档——https://developer.apple.com/documentation/swift/2885064-type

doubleNum = 30.1


func printInfo(_ value: Any) {
let varType = type(of: value)
print("'\(value)' of type '\(varType)'")
}


printInfo(doubleNum)
//'30.1' of type 'Double'

Swift版本4:

print("\(type(of: self)) ,\(#function)")
// within a function of a class

谢谢@Joshua Dance

这就是你如何获得你的对象类型的类型字符串,它是一致的,并考虑到对象定义属于或嵌套的模块。适用于Swift 4.x。

@inline(__always) func typeString(for _type: Any.Type) -> String {
return String(reflecting: type(of: _type))
}


@inline(__always) func typeString(for object: Any) -> String {
return String(reflecting: type(of: type(of: object)))
}


struct Lol {
struct Kek {}
}


// if you run this in playground the results will be something like
typeString(for: Lol.self)    //    __lldb_expr_74.Lol.Type
typeString(for: Lol())       //    __lldb_expr_74.Lol.Type
typeString(for: Lol.Kek.self)//    __lldb_expr_74.Lol.Kek.Type
typeString(for: Lol.Kek())   //    __lldb_expr_74.Lol.Kek.Type

要在迅速< em > < / em >中获得对象类型对象的类别,必须使用类型(:yourObject)

类型(:yourObject)

斯威夫特4:

// "TypeName"
func stringType(of some: Any) -> String {
let string = (some is Any.Type) ? String(describing: some) : String(describing: type(of: some))
return string
}


// "ModuleName.TypeName"
func fullStringType(of some: Any) -> String {
let string = (some is Any.Type) ? String(reflecting: some) : String(reflecting: type(of: some))
return string
}

用法:

print(stringType(of: SomeClass()))     // "SomeClass"
print(stringType(of: SomeClass.self))  // "SomeClass"
print(stringType(of: String()))        // "String"
print(fullStringType(of: String()))    // "Swift.String"