您如何找出对象的类型(在Swift中)?


255

当试图理解一个程序时,或者在某些极端情况下,能够真正找出某种类型的东西很有用。我知道调试器可以向您显示一些类型信息,并且通常可以依靠类型推断来避免在那种情况下不指定类型,但是,我仍然很想拥有类似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()

尽管实际上只是发现任何给定对象类型的一小步

编辑:我已经删除了大量现在不相关的详细信息-如果您有兴趣,请查看编辑历史记录:)



1
有趣的是,print(mc)dump(mc)将显示摘要(您可以从toString(mc)或获取reflect(mc).summary),其中将包含某个地方的类名。但是还不清楚如何自己获得类名。
newacct 2014年

@David相似,但并非所有变量都是类实例。同样,这些问题真的是关于检查类型是否与程序员想要的内容相匹配的,而我希望只是找出类型批发
Jiaaro 2014年


Answers:


284

Swift 3版本:

type(of: yourObject)

8
有趣的事实。这不适用于隐式展开的可选对象!即var myVar: SomeType!。编译器给出错误“无法将'SomeType!.Type'类型的值(aka'ImplicitlyUnwrappedOptional <SomeType> .Type')转换为预期的参数类型'AnyClass'(aka'AnyObject.Type')。编译器建议as! AnyClass在该类型之后添加程序崩溃,并显示一些“ EXC_BAD_INSTRUCTION”和我无法破译的其他残酷现象
LightningStryk

1
实际上,既然Swift 3存在,这应该是公认的答案。谢谢杰里米!
biomiker

1
如果要查找特定的类型名称,则当该类型属于协议类型时,这可能对您不起作用。
克里斯·普林斯

2
如果你有一个String是类型传递Any,然后type(of:)将输出Any,没有String
ScottyBlades

1
@ScottyBlades那么解决方案是什么。你能提供吗?
Mubin Mall

109

Swift 2.0:

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

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

7
这很棒。请注意,如果要镜像的对象是可选类型,则将其与非可选类型进行比较将失败。StringOptional(String)不一样。
Thomas Verbeek

1
正是我在寻找什么,想知道对象的类型是什么
约瑟夫

在这种情况下,有没有一种比较类型在将可选类型与非可选类型进行比较时不会失败
克里斯王子

那就是我想要的。谢谢@Gudbergur。
Mubin Mall

60

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

2
嘿,不确定何时更改,但是正如Alex Pretzlav指出的那样,行为已更改。
加亚罗2014年

1
是。从Swift 3.0开始,该版本subjectType不再可用,并dynamicType导致来自编译器的弃用消息。
拉斐尔

41

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

看起来他们更改了它,以便为您提供类型而不是实例。
加亚罗2014年

@Jiaaro,我用我认为您在原始问题中正在寻找的内容更新了我的答案
Alex Pretzlav 2014年

36

如果仅需要检查变量是否为X类型或是否符合某些协议,则可以使用is,或as?如下所示:

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

这与isKindOfClassObj-C中的等效。

这等效于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>
}

您的答案的第二部分是错误的。as?有条件强制转换的'if let'语句也具有相同的功能isKindOfClass,只是在成功执行时也提供强制转换的结果。
宣布

等价isMemberOfClass于条件object.dynamicType == ClassName.self
2016年

18

斯威夫特3:

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

我认为Swift 3 之前就is存在...?
Nicolas Miari

10

对于Swift 3.0

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

对于Swift 2.0-2.3

String(<Class-Name>)

1
关于此答案对我而言正确的重要一点是,结果字符串与类名完全匹配-因此我可以使用它从NSManagedObject子类中获取核心数据实体名称。我使用了Swift3版本。
肯德尔·赫尔姆斯特·吉尔纳

8

我建议您通过以下两种方式进行操作:

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
print( aShape is Square )is运算符更可取。
DawnSong

对我来说,获取对象类型的好方法。
nihasmata

1

取决于用例。但是,假设您想对“变量”类型做一些有用的事情。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案例。


1

如果您收到“始终为真/失败”的警告,则在使用前可能需要强制转换为“任意” is

(foo as Any) is SomeClass

0
//: 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
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.