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

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 楼

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


Xcode 6.3的更新:

您可以使用_stdlib_getDemangledTypeName()

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>


并将其作为输出:

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


原始答案:

在Xcode 6.3之前,_stdlib_getTypeName的类型名称混乱变量Ewan Swick的博客条目有助于破译这些字符串:

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

Mike Ash的博客条目涵盖了相同的主题。

评论


您如何找到_stdlib_getTypeName()?集成的R​​EPL不再存在,swift-ide-test也不存在,并且Xcode的Swift模块打印会省略_前缀的值。

–莉莉·巴拉德(Lily Ballard)
14年8月21日在19:28

看起来我可以在repl中使用lldb轻松搜索符号。还有_stdlib_getDemangledTypeName()和_stdlib_demangleName()。

–莉莉·巴拉德(Lily Ballard)
2014年8月21日19:40在

噢,整洁,我没有意识到图像是目标模块的捷径。我最终使用了目标模块lookup -r -n _stdlib_,当然更好地表示为image lookup -r -n _stdlib_ libswiftCore.dylib

–莉莉·巴拉德(Lily Ballard)
2014年8月22日在23:13

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

–尼克
2015年10月10日13:57

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

– Szu
16年8月16日在9:26



#2 楼

编辑: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。

评论


我回复了该主题,但我将在这里添加我的评论。这些值的类型为“元类型”,但是似乎没有一种简单的方法可以弄清楚元类型对象代表的类型,这正是我要寻找的。

–马特桥
2014年6月3日,12:53

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

–模拟文件
2014年6月5日在3:09

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

–戴夫·伍德
2014年6月6日18:31

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

–尼克
2015年10月10日13:57

#3 楼

雨燕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实现特别适合枚举案例。有谁知道苹果是否已记录/保证了这种行为?标准库仅包含一条注释,它适合调试...

– Milos
2015年10月5日,9:03

#4 楼



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


它打印“ __NSDate”

更新:请注意,这似乎不再起作用Beta05的

评论


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

–aryaxt
14年8月12日在16:15

对于ObjC类也不起作用(至少在Beta5中),对于NSManagedObject我只能得到“ Builtin.RawPointer = address”

–肯德尔·赫尔姆斯特·盖尔纳(Kendall Helmstetter Gelner)
2014年8月15日在20:36

#5 楼

我当前的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, { 
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.
== "." }) if let typeName = tokens.last { println("Variable \(variable) is of Type \(typeName).") } }


输出:

q4312078q

评论


很好,但是问题还包括可选项,可选项的输出在Optional(...)中设置,因此您的代码将只返回Optional作为Type。它还应该显示var optionalTestVar:Person? = Person(名称:“ Sarah”)为可选(Person),并且与Person一起使用var! as ImplicitlylyUnwrappedOptional(Person)

–西班牙语
2014-09-30 12:50



#6 楼

从带有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
15年2月12日在8:17

@ user965972据我所知,我认为没有任何内容。

–骑
2015年2月14日在3:53



#7 楼

您仍然可以通过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年6月3日在2:33

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

–马特桥
2014年6月3日在2:37

尝试进口可可。

– Leandros
2014年6月3日在2:42

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

–马特桥
2014年6月3日在22:44

className是Cocoa的AppleScript支持的一部分,并且绝对不应该用于常规自省。显然,它在iOS上不可用。

– Nikolai Ruhe
14年7月17日在12:13

#8 楼

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

var classname = reflect(now).summary


评论


在Beta 6中,这使我获得了应用名称+类名称的某种形式的修饰,但这只是一个开始。 +1反映(x)。摘要-谢谢!

–奥利
14年8月24日在16:11

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

– Juan Boero
2015年9月2日在18:10

#9 楼

我很幸运:

let className = NSStringFromClass(obj.dynamicType)


评论


@hdost dude没什么用,但是classForCoder有用。

– Satheesh
15年7月2日在12:07

#10 楼

Xcode 8 Swift 3.0使用类型(of :)

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


#11 楼

在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


#12 楼

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

#13 楼

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

,输出为:


diccionary类型字典
数组的类型为Array
numInt的类型为Int
numFloat的类型为CGFloat
numDouble的类型为Double
classOne的类型为:ClassOne
classTwo是类型:ClassTwo
现在是类型:Date
lbl是类型:UILabel


#14 楼

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

println(now.className)


此属性不适用于不是NSObject子类的普通Swift对象(实际上,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
19年5月16日在15:57

#15 楼

最重要的答案没有使用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'


#16 楼

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

#17 楼

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

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


#18 楼

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

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


评论


注意:description()以。<实际类名称>的格式返回类名称,因此您想用。分割字符串。并获得最后一个令牌。

–MLQ
15年4月17日在10:48

#19 楼

这里的许多答案不适用于最新的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/1187415和stackoverflow.com/a/32087449/1187415有什么问题?两者似乎都可以在Swift 2上很好地工作。(尚未检查其他两个。)

–马丁R
2015年11月11日在18:46



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

– David H
15年11月28日在19:28



#20 楼

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

fr v -d r shipDate


,其输出类似于:

(DBSalesOrderShipDate_DBSalesOrderShipDate_ *) shipDate = 0x7f859940


展开的命令含义如下:

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

知道的一些有用信息是使用“框架变量”输出变量值的方法保证不会执行任何代码。

#21 楼

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

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

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


现在您可以像这样简单地在对象上调用类名:在一个文件中,该文件的名称与您想要的类的名称完全相同。

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


如果需要类(文件)本身中的类名,则可以简单地使用以下行: >
myObject.className



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

#22 楼

根据上面的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
15年2月2日在20:48

我最终在Swift 1.2中使用了以下代码:toString(self.dynamicType).componentsSeparatedByString(“。”)。last !,很明显,我处于对象上下文中并且能够引用self。

–乔
15年5月28日在21:09

#23 楼

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


#24 楼

Swift版本4:

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


感谢@Joshua Dance

#25 楼

Swift 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"


#26 楼

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

例如,似乎没有办法键入:1.something()并获取Int的任意值。 (您可以按照另一个答案的建议,使用something给出提示,但是i.bridgeToObjectiveC().className实际上不是__NSCFNumber的类型-只是它越过Objective-C函数调用的边界时将转换为的类型。)

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

评论


在我的示例中,您能否举一个使用className为变量“ now”打印“ NSDate”的示例,该示例在Xcode6的游乐场中起作用?据我所知,className尚未在Swift对象上定义,即使是那些NSObject的子类。

–马特桥
2014年6月3日22:00

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

–马特桥
2014年6月3日22:46

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

– IPMCC
2014年6月4日12:50

我很好奇REPL如何打印值的类型。

–马特桥
2014年6月4日13:40

REPL几乎可以肯定被解释而不是编译,这很容易解释它

– IPMCC
2014年6月4日14:07

#27 楼

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


#28 楼

要在Swift中获取对象类型或对象类,您必须使用type(of:yourObject)

type(of:yourObject)

#29 楼

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

let object: AnyObject = 1

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


例如。

#30 楼

Xcode 7.3.1,Swift 2.2:

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