如何在 Swift 中打印变量的类型或类?
Posted
技术标签:
【中文标题】如何在 Swift 中打印变量的类型或类?【英文标题】:How do I print the type or class of a variable in Swift? 【发布时间】:2014-07-23 06:10:49 【问题描述】:有没有办法快速打印变量的运行时类型?例如:
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!
。
【问题讨论】:
@JasonMArcher 告诉我,如果您链接的问题在此问题 4 天后被问到,这怎么会是重复的? 关于测试 Swift 对象的类型或读取 Swift 对象的类型有很多问题。我们只是在寻找最好的问题来用作这个主题的“主”问题。建议的副本有一个更彻底的答案。这并不是说您做错了什么,只是我们正在努力减少混乱。 建议的副本没有回答相同的问题; Type.self 不能打印到控制台进行调试,它是用来传递给其他以 Types 为对象的函数。 OT:很奇怪 Swift 没有提供开箱即用的功能,并且需要摆弄如此低级的 C 库。值得报告错误吗? 伙计们,我在下面提供了我的答案。请看一下,让我知道这是否符合预期。 【参考方案1】:您仍然可以通过className
(返回String
)访问该课程。
获取类其实有几种方式,例如classForArchiver
、classForCoder
、classForKeyedArchiver
(都返回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
【讨论】:
我敢打赌这是一个原始的。不幸的是,您无法获得原语的类型。 这些例子在操场上似乎行不通。我得到了原语和非原语的错误。 尝试import Cocoa
。
className 在为 OSX 开发时似乎仅适用于 NSObjects;它在 ios SDK 中不适用于 NSObjects 或“普通”对象
className
是 Cocoa 的 AppleScript 支持的一部分,绝对不应该用于通用内省。显然它在 iOS 上不可用。【参考方案2】:
编辑:一个新的toString
函数has been introduced in Swift 1.2 (Xcode 6.3)。
您现在可以使用.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.self
和yourObject.dynamicType
。
参考:https://devforums.apple.com/thread/227425。
【讨论】:
我回复了那个帖子,但我会在这里添加我的评论。这些值属于“Metatype”类型,但似乎没有一种简单的方法可以确定 Metatype 对象代表什么类型,而这正是我正在寻找的。span> 它们是 Metatype 类型,它们是类。他们代表一个阶级。不确定您的问题到底是什么。someInstance.dynamicType.printClassName()
将打印类的名称。
printClassName() 方法只是他们在文档中的一个示例中创建的示例方法,它不是您可以访问的 API 调用。
仅供参考 - 看起来 toString(...) 已替换为 String(...)【参考方案3】:
似乎没有通用的方法来打印任意值类型的类型名称。正如其他人所指出的,对于 class 实例,您可以打印 value.className
但对于原始值,在运行时,类型信息似乎消失了。
例如,似乎没有办法输入:1.something()
并输出Int
以获得something
的任何值。 (正如另一个答案所建议的那样,您可以使用 i.bridgeToObjectiveC().className
给您一个提示,但 __NSCFNumber
实际上 不是 i
的类型 - 只是当它转换为跨越了 Objective-C 函数调用的边界。)
我很高兴被证明是错误的,但看起来类型检查都是在编译时完成的,并且像 C++(禁用 RTTI)一样,大部分类型信息在运行时都消失了。
【讨论】:
你能举一个例子,在我在 Xcode6 的操场上工作的例子中,使用 className 为变量“now”打印“NSDate”吗?据我所知,在 Swift 对象上没有定义 className,即使是 NSObject 的子类也是如此。 看来 'className' 可用于从 NSObject 继承的对象,但仅在为 mac 开发时,而不是为 iOS 开发。 iOS 似乎根本没有解决方法。 是的,这里真正的收获似乎是,在一般情况下,类型信息在运行时迅速消失。 不过,我很好奇 REPL 是如何打印值的类型的。 REPL 几乎可以肯定是解释而不是编译,这很容易解释它【参考方案4】:当使用 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)
也非常强大。谢谢。【参考方案5】:
这就是你要找的吗?
println("\(object_getClassName(now))");
它打印“__NSDate”
更新:请注意,这似乎不再适用于 Beta05
【讨论】:
不适用于快速课程。返回“UnsafePointer(0x7FBB18D06DE0)” 也不适用于 ObjC 类(至少在 Beta5 中),对于 NSManagedObject 我只得到“Builtin.RawPointer = address”【参考方案6】:您可以使用反射来获取有关对象的信息。 例如对象类的名称:
var classname = reflect(now).summary【讨论】:
在 Beta 6 中,这让我得到了应用程序名称 + 类名称,但形式略有不同,但这是一个开始。 +1reflect(x).summary
-- 谢谢!
不起作用,在 Swift 1.2 Xcode 6.4 和 iOs 8.4 中打印一个空行【参考方案7】:
在 lldb beta 5 中,您可以使用以下命令查看对象的类:
fr v -d r shipDate
输出类似:
(DBSalesOrderShipDate_DBSalesOrderShipDate_ *) shipDate = 0x7f859940
扩展出来的命令意思是这样的:
Frame Variable
(打印帧变量)-d run_target
(扩展动态类型)
需要了解的一点是,使用“帧变量”输出变量值可以保证不会执行任何代码。
【讨论】:
【参考方案8】:2016 年 9 月更新
Swift 3.0:使用type(of:)
,例如type(of: someThing)
(因为 dynamicType
关键字已被移除)
2015 年 10 月更新:
我将下面的示例更新为新的 Swift 2.0 语法(例如,println
被替换为 print
,toString()
现在是 String()
)。
来自 Xcode 6.3 发行说明:
@nschum 在 cmets 中指出 Xcode 6.3 release notes 显示了另一种方式:
类型值现在在与 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's blog entry 有助于破译这些字符串:
例如_TtSi
代表 Swift 内部的 Int
类型。
Mike Ash has a great blog entry covering the same topic.
【讨论】:
你是怎么找到_stdlib_getTypeName()
的?集成的 REPL 不再存在,swift-ide-test
也不存在,Xcode 打印的 Swift 模块省略了 _
前缀值。
看起来我可以在 repl 中轻松搜索带有 lldb
的符号。还有_stdlib_getDemangledTypeName()
和_stdlib_demangleName()
。
哦,太好了,我没有意识到image
是target modules
的快捷方式。我最终使用了target modules lookup -r -n _stdlib_
,当然更好地表达为image lookup -r -n _stdlib_ libswiftCore.dylib
仅供参考 - 看起来 toString(...) 已替换为 String(...)。
Swift 3.0:dynamicType
关键字已从 Swift 中删除。取而代之的是一个新的原始函数 type(of:)
已添加到语言中:github.com/apple/swift/blob/master/CHANGELOG.md【参考方案9】:
我很幸运:
let className = NSStringFromClass(obj.dynamicType)
【讨论】:
@hdost 老兄什么都没用,但 classForCoder 工作了。【参考方案10】:我当前的 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.
【讨论】:
好,但问题还包括 Optionals,options 的输出设置在Optional(...)
,因此您的代码只会返回 Optional
作为 Type。它还应该将var optionalTestVar: Person? = Person(name:"Sarah")
显示为 Optional(Person) 并显示为 Person 的 var!作为 ImplicitlyUnwrappedOptional(Person)【参考方案11】:
我在这里尝试了其他一些答案,但 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。
【讨论】:
【参考方案12】:我找到了自行开发课程的解决方案(或者您可以访问的)。
在您的对象类定义中放置以下计算属性:
var className: String?
return __FILE__.lastPathComponent.stringByDeletingPathExtension
现在您可以像这样简单地调用对象上的类名:
myObject.className
请注意,这只有在您的类定义是在一个名称完全与您想要的类名称相同的文件中进行时才有效。
因为这是通常这种情况上面的答案应该适用于大多数情况。但在一些特殊情况下,您可能需要找出不同的解决方案。
如果您需要类名在类(文件)本身内,您可以简单地使用这一行:
let className = __FILE__.lastPathComponent.stringByDeletingPathExtension
也许这种方法可以帮助一些人。
【讨论】:
【参考方案13】:根据上面 Klass 和 Kevin Ballard 给出的答案和 cmets,我会选择:
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"
【讨论】:
我认为这是更好的答案。 我最终在 Swift 1.2 中使用了以下内容:toString(self.dynamicType).componentsSeparatedByString(".").last!
,显然我在对象上下文中并且能够引用 self
。【参考方案14】:
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)")
【讨论】:
【参考方案15】:我找到了这个解决方案,希望它可能适用于其他人。 我创建了一个类方法来访问该值。请记住,这仅适用于 NSObject 子类。但至少是一个干净整洁的解决方案。
class var className: String!
let classString : String = NSStringFromClass(self.classForCoder())
return classString.componentsSeparatedByString(".").last;
【讨论】:
【参考方案16】:从带有 Swift 1.2 的 Xcode 6.3 开始,您可以简单地将类型值转换为完全解构的 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 据我所知,我认为没有。【参考方案17】:SWIFT 5
在最新版本的 Swift 3 中,我们可以通过 String
初始化器获得类型名称的漂亮描述。比如print(String(describing: type(of: object)))
。其中object
可以是实例变量,例如数组、字典、Int
、NSDate
、自定义类的实例等。
这是我的完整答案:Get class name of object as string in 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()
输出是:
字典是字典类型 数组是数组类型 numInt 是 Int 类型 numFloat 是 CGFloat 类型 numDouble 是 Double 类型 classOne 的类型为:ClassOne classTwo 的类型为:ClassTwo 现在是类型:日期 lbl 的类型为:UILabel【讨论】:
【参考方案18】:在带有 Swift 1.2 的最新 XCode 6.3 中,这是我发现的唯一方法:
if view.classForCoder.description() == "UISegment"
...
【讨论】:
注意: description() 以<swift module name>.<actual class name>
格式返回类名,因此您想用.
拆分字符串并获取最后一个标记。
【参考方案19】:
不完全是你所追求的,但你也可以像这样检查变量的类型和 Swift 类型:
let object: AnyObject = 1
if object is Int
else if object is String
例如。
【讨论】:
【参考方案20】:斯威夫特 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 2 的实现特别适合枚举情况。有谁知道Apple是否记录/保证了这种行为?标准库仅包含一个适合调试的注释...【参考方案21】:此处的许多答案不适用于最新的 Swift(撰写本文时为 Xcode 7.1.1)。
当前获取信息的方法是创建一个Mirror
并对其进行询问。对于类名,它很简单:
let mirror = Mirror(reflecting: instanceToInspect)
let classname:String = mirror.description
还可以从Mirror
检索有关对象的其他信息。详情请见http://swiftdoc.org/v2.1/type/Mirror/。
【讨论】:
***.com/a/25345480/1187415 和***.com/a/32087449/1187415 的答案有什么问题?两者似乎都适用于 Swift 2。(尚未检查其他的。) 我喜欢这个作为一个通用的解决方案,但是获取“ViewController 的镜像”(无关的“镜像”) - 等等,看起来使用“.subjectType”可以解决问题!【参考方案22】:Xcode 7.3.1、Swift 2.2:
String(instanceToPrint.self).componentsSeparatedByString(".").last
【讨论】:
【参考方案23】:在 Swift 3.0 中,您可以使用 type(of:)
,因为 dynamicType
关键字已被删除。
【讨论】:
【参考方案24】:Xcode 8 Swift 3.0 使用 type(of:)
let className = "\(type(of: instance))"
【讨论】:
【参考方案25】:在 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
【讨论】:
【参考方案26】: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")
【讨论】:
【参考方案27】:这在检查对象是否是类的类型时也很方便:
if someObject is SomeClass
//someObject is a type of SomeClass
【讨论】:
【参考方案28】:请查看下面的代码 sn-p,如果您正在寻找类似下面的内容,请告诉我。
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)")
【讨论】:
【参考方案29】:影响从String(describing: type(of: self))
返回的类名的另一个重要方面是访问控制。
考虑以下示例,基于 Swift 3.1.1、Xcode 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
表示。如果类具有private
或fileprivate
访问控制级别,Swift 似乎会附加某种与相关类的“嵌套”类相关的标识符。
PrivateDemoSystemCall
和FileprivateDemoSystemCall
的结果是附加了相同的标识符,因为它们都嵌套在同一个父类中。
除了一些 hacky 替换或正则表达式函数之外,我还没有找到摆脱它的方法。
只要我的 2 美分。
【讨论】:
【参考方案30】:最佳答案没有使用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 中打印变量的类型或类?的主要内容,如果未能解决你的问题,请参考以下文章