你如何找到一个对象的类型(在Swift中)?

在试图理解一个程序时,或者在某些极端情况下,找出某个东西的类型是很有用的。我知道调试器可以向你显示一些类型信息,在这些情况下,你通常可以依靠类型推断来避免不指定类型,但我仍然非常希望有类似Python的type()这样的东西

dynamicType(参见这个问题)

更新:在Swift的最新版本中已经更改,obj.dynamicType现在给你一个对类型的引用,而不是动态类型的实例。

这款似乎最有希望,但到目前为止我还没能找到实际的型号。

class MyClass {
var count = 0
}


let mc = MyClass()


# update: this now evaluates as true
mc.dynamicType === MyClass.self

我还尝试使用类引用来实例化一个新对象,可以工作,但奇怪的是,它给了我一个错误,告诉我必须添加required初始化器:

工作原理:

class MyClass {
var count = 0
required init() {
}
}


let myClass2 = MyClass.self
let mc2 = MyClass2()

不过,要真正发现任何给定对象的类型,这仍然只是一小步

编辑:我已经删除了大量现在不相关的细节-如果你感兴趣,看看编辑历史:)

257714 次浏览

dynamicType.printClassName代码来自Swift书籍中的一个示例。我不知道如何直接获取自定义类名,但你可以使用is关键字检查实例类型,如下所示。这个例子还展示了如何实现一个自定义的className函数,如果你真的想要类名作为字符串。

class Shape {
class func className() -> String {
return "Shape"
}
}


class Square: Shape {
override class func className() -> String {
return "Square"
}
}


class Circle: Shape {
override class func className() -> String {
return "Circle"
}
}


func getShape() -> Shape {
return Square() // hardcoded for example
}


let newShape: Shape = getShape()
newShape is Square // true
newShape is Circle // false
newShape.dynamicType.className() // "Square"
newShape.dynamicType.className() == Square.className() // true

注意:
表示NSObject的子类已经实现了自己的className函数。如果您正在使用Cocoa,您可以使用这个属性。

class MyObj: NSObject {
init() {
super.init()
println("My class is \(self.className)")
}
}
MyObj()

Xcode 6.0.1中开始(至少,不确定添加它的时间),您的原始示例现在可以工作:

class MyClass {
var count = 0
}


let mc = MyClass()
mc.dynamicType === MyClass.self // returns `true`

更新:

要回答最初的问题,您实际上可以成功地将Objective-C运行时与普通Swift对象一起使用。

试试下面的方法:

import Foundation
class MyClass { }
class SubClass: MyClass { }


let mc = MyClass()
let m2 = SubClass()


// Both of these return .Some("__lldb_expr_35.SubClass"), which is the fully mangled class name from the playground
String.fromCString(class_getName(m2.dynamicType))
String.fromCString(object_getClassName(m2))
// Returns .Some("__lldb_expr_42.MyClass")
String.fromCString(object_getClassName(mc))

如果你只是需要检查变量是否属于X类型,或者它是否符合某种协议,那么你可以使用is,或as?,如下所示:

var unknownTypeVariable = …


if unknownTypeVariable is <ClassName> {
//the variable is of type <ClassName>
} else {
//variable is not of type <ClassName>
}

这相当于Obj-C中的isKindOfClass

它等价于conformsToProtocolisMemberOfClass

var unknownTypeVariable = …


if let myClass = unknownTypeVariable as? <ClassName or ProtocolName> {
//unknownTypeVarible is of type <ClassName or ProtocolName>
} else {
//unknownTypeVariable is not of type <ClassName or ProtocolName>
}

以下是我推荐的两种方法:

if let thisShape = aShape as? Square

或者:

aShape.isKindOfClass(Square)

下面是一个详细的例子:

class Shape { }
class Square: Shape { }
class Circle: Shape { }


var aShape = Shape()
aShape = Square()


if let thisShape = aShape as? Square {
println("Its a square")
} else {
println("Its not a square")
}


if aShape.isKindOfClass(Square) {
println("Its a square")
} else {
println("Its not a square")
}

斯威夫特2.0:

进行这种类型自省的正确方法是使用镜结构

    let stringObject:String = "testing"
let stringArrayObject:[String] = ["one", "two"]
let viewObject = UIView()
let anyObject:Any = "testing"


let stringMirror = Mirror(reflecting: stringObject)
let stringArrayMirror = Mirror(reflecting: stringArrayObject)
let viewMirror = Mirror(reflecting: viewObject)
let anyMirror = Mirror(reflecting: anyObject)

然后,要从Mirror结构体中访问类型本身,可以使用属性subjectType,如下所示:

    // Prints "String"
print(stringMirror.subjectType)


// Prints "Array<String>"
print(stringArrayMirror.subjectType)


// Prints "UIView"
print(viewMirror.subjectType)


// Prints "String"
print(anyMirror.subjectType)

然后你可以这样使用:

    if anyMirror.subjectType == String.self {
print("anyObject is a string!")
} else {
print("anyObject is not a string!")
}

这取决于用例。但是让我们假设您想要对“变量”类型做一些有用的事情。Swift switch语句非常强大,可以帮助你得到你想要的结果…

    let dd2 = ["x" : 9, "y" : "home9"]
let dds = dd2.filter {
let eIndex = "x"
let eValue:Any = 9
var r = false


switch eValue {
case let testString as String:
r = $1 == testString
case let testUInt as UInt:
r = $1 == testUInt
case let testInt as Int:
r = $1 == testInt
default:
r = false
}


return r && $0 == eIndex
}
在这种情况下,有一个简单的字典,其中包含键/值对,可以是UInt, Int或String。在字典上的.filter()方法中,我需要确保正确地测试值,并且只在字符串时测试字符串,等等。switch语句使此操作简单而安全! 通过将9赋值给Any类型的变量,它将执行Int类型的转换。尝试将其更改为:

   let eValue:Any = "home9"

..再试一次。这一次它执行as String大小写。

如果你得到一个“always true/fails”的警告,你可能需要在使用is之前强制转换为Any

(foo as Any) is SomeClass
//: Playground - noun: a place where people can play


import UIKit


class A {
class func a() {
print("yeah")
}


func getInnerValue() {
self.dynamicType.a()
}
}


class B: A {
override class func a() {
print("yeah yeah")
}
}


B.a() // yeah yeah
A.a() // yeah
B().getInnerValue() // yeah yeah
A().getInnerValue() // yeah

Swift 3版本:

type(of: yourObject)

针对Swift 3.0

String(describing: <Class-Name>.self)

Swift 2.0 - 2.3

String(<Class-Name>)

斯威夫特3:

if unknownType is MyClass {
//unknownType is of class type MyClass
}

如果一个参数作为Any传递给你的函数,你可以测试一个特殊的类型,像这样:

   func isADate ( aValue : Any?) -> Bool{
if (aValue as? Date) != nil {
print ("a Date")
return true
}
else {
print ("This is not a date ")
return false
}
}

老问题,但这符合我的需要(Swift 5.x):

print(type(of: myObjectName))

备注:我不明白@JérémyLapointe如何回答这个问题。使用type(of:)只能通过检查编译时信息来工作,即使实际类型是一个更特定的子类。现在有一种更简单的方法来动态查询斯威夫特5.1中的类型,而不必像@Dash建议的那样求助于dynamicType。有关我从哪里得到这个信息的更多详细信息,请参见扩展Swift Self到类成员和值类型


代码

斯威夫特5.1

// Within an instance method context
Self.self


// Within a static method context
self

这允许使用Self作为引用包含类型(在__abc1、__abc2和final class的情况下)或动态类型(在非-final classes的情况下)的简写。

解释

提案很好地解释了为什么这种方法改进了dynamicType:

引入Self可以解决以下问题:

  • dynamicType仍然是Swift小写关键字规则的例外。此更改消除了与之不一致的特殊情况 斯威夫特的新标准。Self的意图更短、更清晰。它 镜像self,它指向当前实例
  • 它提供了一种更简单的访问静态成员的方法。类型名越大,可读性就越差。 MyExtremelyLargeTypeName.staticMember难以输入和读取
  • 使用硬连接类型名的代码的可移植性不如自动知道其类型的代码。
  • 重命名类型意味着在代码中更新任何TypeName引用。使用self.dynamicType与Swift的简洁和 清晰之处在于它既嘈杂又深奥

注意self.dynamicType.classMemberTypeName.classMember . 0