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


335

有没有一种方法可以快速打印变量的运行时类型?例如:

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!


43
@JasonMArcher告诉我,如果您链​​接的问题在此问题提出后4天被问到,那么该副本如何重复?
akashivskyy 2014年

关于测试Swift对象的类型或读取Swift对象的类型有很多问题。我们正在寻找最佳问题,以用作该主题的“主要”问题。建议的副本有一个更彻底的答案。这并不是说您做错了什么,而是我们正在努力减少混乱。
JasonMArcher 2014年

4
建议的重复项无法回答相同的问题;Type.self不能出于调试目的而打印到控制台,它意在用于传递给以Types为对象的其他函数。
马特·布里奇斯

2
OT:很奇怪,Swift没有提供开箱即用的功能,并且需要弄乱如此低级的C库。值得提交错误报告吗?
qwerty_so 2014年

伙计们,我在下面提供了我的答案。请看看,让我知道这是否是预期的。
DILIP KOSURI

Answers:


377

2016年9月更新

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

2015年10月更新

我将以下示例更新为新的Swift 2.0语法(例如,println已被替换为printtoString()现在是String())。

从Xcode 6.3发行说明

@nschum在注释中指出Xcode 6.3发行说明显示了另一种方式:

现在,当与println或字符串插值一起使用时,类型值将打印为完整的取消组合类型名称。

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已经获得了变量的变形类型名称。Ewan Swick的博客条目有助于破译以下字符串:

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

迈克·阿什(Mike Ash)在同一个主题的博客文章中非常出色


2
你是怎么找到的_stdlib_getTypeName()?集成的R​​EPL不再存在,swift-ide-test也不存在,并且Xcode的Swift模块打印_省略了前缀值。
莉莉·巴拉德

10
看起来我可以lldb轻松地在repl中搜索符号。还有_stdlib_getDemangledTypeName()_stdlib_demangleName()
莉莉·巴拉德

1
噢,整洁,我没有意识到这image是的捷径target modules。我最终使用target modules lookup -r -n _stdlib_,当然更好地表示为image lookup -r -n _stdlib_ libswiftCore.dylib
Lily Ballard 2014年

1
仅供参考-看起来toString(...)已替换为String(...)。
尼克

6
Swift 3.0:dynamicType关键字已从Swift中删除。取而代之的type(of:)是,该语言中添加了一个新的原始函数:github.com/apple/swift/blob/master/CHANGELOG.md
Szu

46

编辑:Swift 1.2(Xcode 6.3)中引入了一个新toString功能。

现在,您可以使用.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


2
我回应了该主题,但我将在此处添加我的评论。这些值的类型为“元类型”,但是似乎没有一种简单的方法可以弄清楚元类型对象代表的类型,这就是我要寻找的。
马特·布里奇斯

它们是Metatype类型,并且是类。他们代表一个阶级。不确定到底是什么问题。someInstance.dynamicType.printClassName()将打印类的名称。
模拟文件

18
printClassName()方法只是他们在文档中的一个示例中创建的示例方法,它不是您可以访问的API调用。
戴夫·伍德

4
仅供参考-看起来toString(...)已替换为String(...)
Nick

33

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

1
Swift 2实现特别适合枚举案例。有谁知道苹果是否已记录/保证了这种行为?标准库仅包含一条注释,它适合调试...
milos 2015年

30

这是您要找的东西吗?

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

打印“ __NSDate”

更新:请注意,自Beta05起,此功能似乎不再起作用


2
不适用于快速班。返回“ UnsafePointer(0x7FBB18D06DE0)”
aryaxt 2014年

4
也不适用于ObjC类(至少在Beta5中),对于NSManagedObject,我只会得到“ Builtin.RawPointer = address”
Kendall Helmstetter Gelner 2014年

23

我当前的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.

1
很好,但问题还包括Optionals,在中设置了optionals的输出Optional(...),因此您的代码将仅Optional作为Type 返回。它也应该显示var optionalTestVar: Person? = Person(name:"Sarah")为Optional(Person)并与Person一起显示var!as ImplicitlylyUnwrappedOptional(Person)
Binarian

18

从带有Swift 1.2的Xcode 6.3开始,您可以简单地将类型值转换为完整的demangled 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"

1.2中的另一个方向是否可能?要基于类名称初始化实例?
user965972

@ user965972据我所知,我认为没有任何东西。
开玩笑

17

您仍然可以通过访问类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

我敢打赌这是原始的。不幸的是,您无法获得基本类型。
Leandros 2014年

这些示例似乎在操场上不起作用。对于基本体和非基本体,我都会收到错误消息。
马特·布里奇斯

试着去import Cocoa
Leandros 2014年

9
当为OSX开发时,className似乎仅可用于NSObjects。它在iOS SDK中不适用于NSObjects或“正常”对象
Matt Bridges 2014年

3
className是Cocoa的AppleScript支持的一部分,绝对不应用于常规自省。显然,它在iOS上不可用。
Nikolai Ruhe 2014年

16

您可以使用反射来获取有关对象的信息。
例如对象类的名称:

var classname = reflect(now).summary

在Beta 6中,这使我获得了应用名称+类名称,但格式有些许混乱,但这只是一个开始。+1,reflect(x).summary谢谢!
Olie

不起作用,在Swift 1.2 Xcode 6.4和iOs 8.4中打印空白行
Juan Boero



10

在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


7

SWIFT 5

在最新版本的Swift 3中,我们可以通过String初始化器获得漂亮的类型名描述。例如print(String(describing: type(of: object)))。在哪里object 可以是实例变量,例如数组,字典,an Int,a NSDate,自定义类的实例等。

这是我的完整答案: 在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()

输出为:

  • diccionary类型为Dictionary
  • 数组是数组类型
  • numInt的类型为Int
  • numFloat是CGFloat类型
  • numDouble是Double类型的
  • classOne是类型:ClassOne
  • classTwo的类型:ClassTwo
  • 现在的类型:日期
  • lbl类型:UILabel

5

使用Cocoa(而不是CocoaTouch)时,可以将className属性用于NSObject子类的对象。

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)似乎可以解决问题。


即使从Xcode 10和Swift 4.2开始,它dump(object)也确实非常强大。谢谢。
Alex Zavatone

5

最重要的答案没有使用的新方法的有效示例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'

4

我在这里尝试了其他一些答案,但是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。


4

我找到了这个解决方案,希望可以对其他人有用。我创建了一个访问值的类方法。请记住,这仅适用于NSObject子类。但是至少是一个干净整洁的解决方案。

class var className: String!{
    let classString : String = NSStringFromClass(self.classForCoder())
    return classString.componentsSeparatedByString(".").last;
}

4

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

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

注意: description()以格式返回类名<swift module name>.<actual class name>,因此您想用a拆分字符串.并获取最后一个标记。
马修·奎罗斯

4

这里的许多答案不适用于最新的Swift(在撰写本文时,Xcode 7.1.1)。

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

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

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


答案stackoverflow.com/a/25345480/1187415stackoverflow.com/a/32087449/1187415有什么问题?两者似乎都可以在Swift 2上很好地工作。(尚未检查其他两个。)
Martin R

我喜欢将其作为常规解决方案,但是获得“ Mirror for ViewController”(外部“ Mirror for”)-等待,就像使用“ .subjectType”一样!
David H

3

从beta 5开始,在lldb中,您可以使用以下命令查看对象的类:

fr v -d r shipDate

输出类似:

(DBSalesOrderShipDate_DBSalesOrderShipDate_ *) shipDate = 0x7f859940

展开的命令意味着:

Frame Variable(打印框架变量)-d run_target(展开动态类型)

有一点有用的知识是,使用“框架变量”输出变量值可确保不执行任何代码。


3

我找到了自行开发课程解决方案(或者您可以访问的)。

将以下计算所得的属性放入对象类定义中:

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

现在,您可以像这样简单地在您的对象上调用类名称:

myObject.className

请注意,这仅在您的类定义是在名称完全相同的文件中进行时才有效的名称与您要的类。

由于通常是这种情况,因此上述答案在大多数情况下都可以做到。但是在某些特殊情况下,您可能需要找出其他解决方案。


如果您需要类(文件)本身内的类名,则可以使用以下行:

let className = __FILE__.lastPathComponent.stringByDeletingPathExtension

也许这种方法可以帮助一些人。


3

根据以上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"

我认为这是更好的答案。
maschall'2

我最终在Swift 1.2中使用了以下内容: toString(self.dynamicType).componentsSeparatedByString(".").last!显然,我处于对象上下文中并且能够引用self

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

3

Swift版本4:

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

谢谢@约书亚舞


3

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

2

似乎没有通用的方法来打印任意值类型的类型名称。正如其他人指出的那样,对于实例,您可以打印value.className但对于原始值,似乎在运行时类型信息已消失。

例如,似乎没有办法键入:1.something()并获取Int任何值something。(可以作为另一个答案建议,利用i.bridgeToObjectiveC().className给你一个提示,但是__NSCFNumber不是真正的类型i-正是它会被转换为当它穿过一个Objective-C函数调用的边界)

我很高兴被证明是错误的,但是看起来类型检查全部在编译时完成,并且像C ++(禁用了RTTI)一样,许多类型信息在运行时都消失了。


在我的示例中,您能否举一个使用className为变量“ now”打印“ NSDate”的示例,该示例在Xcode6的操场上工作?据我所知,className尚未在Swift对象上定义,即使是NSObject子类的对象也没有定义。
Matt Bridges 2014年

看来“ className”可用于源自NSObject的对象,但仅适用于Mac开发,不适用于iOS。iOS似乎根本没有解决方法。
马特·布里奇斯

1
是的,这里真正的收获似乎是,在一般情况下,类型信息在运行时迅速消失了。
ipmcc 2014年

我很好奇REPL如何打印值的类型。
Matt Bridges 2014年

该REPL几乎可以肯定的解释,而不是编译的,这很容易解释
ipmcc

2

这是您获取对象类型的类型字符串的方式,该类型字符串是一致的,并考虑了对象定义属于或嵌套在哪个模块中。在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


1

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

let object: AnyObject = 1

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

例如。


1

Xcode 7.3.1,Swift 2.2:

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

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.