ChatGPT解决这个技术问题 Extra ChatGPT

如何在 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!

@JasonMArcher 告诉我,如果您链接的问题是在此问题后 4 天提出的,这怎么会是重复的?
关于测试 Swift 对象的类型或读取 Swift 对象的类型有很多问题。我们只是在寻找最好的问题来用作这个主题的“主”问题。建议的副本有一个更彻底的答案。这并不是说你做错了什么,只是我们正在努力减少混乱。
建议的副本没有回答相同的问题; Type.self 不能出于调试目的打印到控制台,它旨在用于传递给其他将 Types 作为对象的函数。
OT:很奇怪 Swift 没有提供开箱即用的功能,而且需要摆弄如此低级的 C 库。值得报告错误吗?
伙计们,我在下面提供了我的答案。请看一下,让我知道这是否是预期的。

i
iwasrobbed

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 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()
哦,太好了,我没有意识到 imagetarget 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
a
akashivskyy

编辑:新的 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.selfyourObject.dynamicType

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


我回复了那个帖子,但我会在这里添加我的评论。这些值属于“Metatype”类型,但似乎没有一种简单的方法可以确定 Metatype 对象代表什么类型,而这正是我正在寻找的。
它们是 Metatype 类型,它们是类。他们代表一个阶级。不知道你的问题到底是什么。 someInstance.dynamicType.printClassName() 将打印类的名称。
printClassName() 方法只是他们在文档中的一个示例中创建的示例方法,它不是您可以访问的 API 调用。
仅供参考 - 看起来 toString(...) 已替换为 String(...)
E
Evgenii

斯威夫特 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是否记录/保证了这种行为?标准库仅包含一条注释,说明这适用于调试...
H
Haiku Oezu

这是你要找的吗?

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

它打印“__NSDate”

更新:请注意,这似乎不再适用于 Beta05


不适用于快速课程。返回“不安全指针(0x7FBB18D06DE0)”
也不适用于 ObjC 类(至少在 Beta5 中),对于 NSManagedObject 我只得到“Builtin.RawPointer = address”
M
Matt Hagen

我当前的 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)
k
kiding

从带有 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 据我所知,我认为没有。
L
Leandros

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

我敢打赌这是一个原始的。不幸的是,您无法获得原语的类型。
这些例子似乎在操场上不起作用。我得到了原语和非原语的错误。
尝试 import Cocoa
在为 OSX 开发时,className 似乎仅适用于 NSObjects;它在 iOS SDK 中不适用于 NSObjects 或“普通”对象
className 是 Cocoa 的 AppleScript 支持的一部分,绝对不应该用于通用内省。显然它在 iOS 上不可用。
S
Stan

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

var classname = reflect(now).summary

在 Beta 6 中,这让我得到了应用程序名称 + 类名称,但形式略有不同,但这是一个开始。 +1 为 reflect(x).summary - 谢谢!
不起作用,在 Swift 1.2 Xcode 6.4 和 iOs 8.4 中打印一个空行
H
Hari Kunwar

Xcode 8 Swift 3.0 使用 type(of:)

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

m
mxcl

我很幸运:

let className = NSStringFromClass(obj.dynamicType)

@hdost 伙计没有任何效果,但 classForCoder 有效。
C
Community

在 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

B
Bruno Serrano

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


m
mauricioconde

斯威夫特 5

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

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


M
Matt Bridges

使用 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) 也非常强大。谢谢你。
J
Joshua Dance

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

J
James Jones

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


D
DennyLou

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

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

J
Joel Kopelioff

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

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

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

这里的许多答案不适用于最新的 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。(尚未检查其他的。)
我喜欢这个作为一个通用的解决方案,但是获得“ViewController 的镜像”(无关的“镜像”) - 等等,看起来使用“.subjectType”可以解决问题!
R
Rahul Panzade

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

类型(属于:你的对象)


K
Kendall Helmstetter Gelner

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

fr v -d r shipDate

输出类似:

(DBSalesOrderShipDate_DBSalesOrderShipDate_ *) shipDate = 0x7f859940

扩展的命令意味着类似:

Frame Variable(打印帧变量)-d run_target(扩展动态类型)

需要了解的一点是,使用“帧变量”输出变量值可以保证不执行任何代码。


J
Jeehut

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

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

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

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

myObject.className

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

由于这通常是这种情况,因此上述答案应该适用于大多数情况。但在某些特殊情况下,您可能需要找出不同的解决方案。

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

let className = __FILE__.lastPathComponent.stringByDeletingPathExtension

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


V
Vince O'Sullivan

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

我认为这是更好的答案。
我最终在 Swift 1.2 中使用了以下内容:toString(self.dynamicType).componentsSeparatedByString(".").last!,显然我在对象上下文中并且能够引用 self
A
Alex Burla
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)")

d
dengApro

斯威夫特版本 4:

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

谢谢@Joshua Dance


D
Desmond Hume

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

i
ipmcc

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

例如,看起来好像没有办法输入:1.something() 并为 something 的任何值输出 Int。 (正如另一个答案所建议的那样,您可以使用 i.bridgeToObjectiveC().className 给您一个提示,但 __NSCFNumber 不是实际上是 i 的类型 - 只是当它转换为跨越了 Objective-C 函数调用的边界。)

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


您能否举一个在我的示例中使用 className 为变量“now”打印“NSDate”的示例,该示例适用于 Xcode6 的操场?据我所知,className 没有在 Swift 对象上定义,即使是 NSObject 的子类。
似乎“className”可用于从 NSObject 继承的对象,但仅在为 mac 开发时才可用,而不是为 iOS 开发。 iOS似乎根本没有解决方法。
是的,这里真正的收获似乎是,在一般情况下,类型信息在运行时迅速消失。
不过,我很好奇 REPL 如何能够打印值的类型。
REPL 几乎可以肯定是解释的而不是编译的,这很容易解释它
I
Igor Muzyka

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

P
PersuitOfPerfection

不完全是您所追求的,但您还可以根据 Swift 类型检查变量的类型,如下所示:

let object: AnyObject = 1

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

例如。


l
leanne

Xcode 7.3.1,斯威夫特 2.2:

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