ChatGPT解决这个技术问题 Extra ChatGPT

How do you find out the type of an object (in Swift)?

When trying to understand a program, or in some corner-cases, it's useful to find out what type something is. I know the debugger can show you some type information, and you can usually rely on type inference to get away with not specifying the type in those situations, but still, I'd really like to have something like Python's type()

dynamicType (see this question)

Update: this has been changed in a recent version of Swift, obj.dynamicType now gives you a reference to the type and not the instance of the dynamic type.

This one seems the most promising, but I haven't been able to find out the actual type so far.

class MyClass {
    var count = 0
}

let mc = MyClass()

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

I also tried using a class reference to instantiate a new object, which does work, but oddly gave me an error saying I must add a required initializer:

works:

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

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

Still only a small step toward actually discovering the type of any given object though

edit: I've removed a substantial number of now irrelevant details - look at the edit history if you're interested :)

Interestingly, print(mc) or dump(mc) will print a summary (which you can get from toString(mc) or reflect(mc).summary), which will contain the class name in there somewhere. But it's not clear how to get just the class name yourself.
@David similar, but not all variables are class instances. Also that questions was really about checking if the type matches what the programmer is looking for, whereas I'm hoping to just find out the type wholesale

R
Randika Vishman

Swift 3 version:

type(of: yourObject)

Fun fact. This does not work with implicitly unwrapped optionals! i.e. var myVar: SomeType!. Compiler gives the error "Cannot convert value of type 'SomeType!.Type' (aka 'ImplicitlyUnwrappedOptional<SomeType>.Type') to expected argument type 'AnyClass' (aka 'AnyObject.Type') Compiler suggest adding as! AnyClass after the type but then program crashes with some "EXC_BAD_INSTRUCTION" and other jiberrish that I cannot decipher.
Indeed, this should be the accepted answer now that Swift 3 exists. Thank you Jeremy!
If you are looking for the specific type name, when the type is of a protocol type, this may not work for you.
If you have a String that is passed as type Any then type(of:) will output Any, not String.
if type(of: yourObject) == MyObjectClass.self { ... }
R
Randika Vishman

Swift 2.0:

The proper way to do this kind of type introspection would be with the Mirror struct,

    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)

Then to access the type itself from the Mirror struct you would use the property subjectType like so:

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

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

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

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

You can then use something like this:

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

This is great. Beware that if the object being mirrored is of optional type, then comparing it to a non-optional type will fail. String and Optional(String) are not the same.
Exactly what i was looking for, wanted to know what is the type of the object
Is there a type of comparison in this context that will not fail when comparing optional against non-optional types?
That's what I was looking for. Thank you @Gudbergur.
This is sweet! But yeah definitely you should add for optional use case (ie: let anyObject: Any? = "testing"), check for nil -> if not nil -> make non-optional -> and continue with this answer as regular
R
Randika Vishman

The dynamicType.printClassName code is from an example in the Swift book. There's no way I know of to directly grab a custom class name, but you can check an instances type using the is keyword as shown below. This example also shows how to implement a custom className function, if you really want the class name as a string.

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

Note:
that subclasses of NSObject already implement their own className function. If you're working with Cocoa, you can just use this property.

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

Hey, not sure when it changed, but as Alex Pretzlav pointed out, the behavior has changed.
Yes. As of Swift 3.0, subjectType is no longer available, and dynamicType causes a deprecation message from the compiler.
R
Randika Vishman

As of Xcode 6.0.1 (at least, not sure when they added it), your original example now works:

class MyClass {
    var count = 0
}

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

Update:

To answer the original question, you can actually use the Objective-C runtime with plain Swift objects successfully.

Try the following:

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

Looks like they changed it to give you the type instead of an instance.
@Jiaaro, I updated my answer with what I think that you were looking for in your original question
d
daedalus

If you simply need to check whether the variable is of type X, or that it conforms to some protocol, then you can use is, or as? as in the following:

var unknownTypeVariable = …

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

This is equivalent of isKindOfClass in Obj-C.

And this is equivalent of conformsToProtocol, or isMemberOfClass

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

The second part of your answer is wrong. The 'if let' statement with as? conditional cast does the same as isKindOfClass as well, just also provides the result of the cast should it Succeed.
The equivalent of isMemberOfClass is the condition object.dynamicType == ClassName.self.
R
Randika Vishman

Swift 3:

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

I think is exists from before Swift 3...?
R
Randika Vishman

For Swift 3.0

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

For Swift 2.0 - 2.3

String(<Class-Name>)

The important thing about this answer being correct for me, is that the resulting string exactly matches the class name - so I can use this to get a Core Data entity name from an NSManagedObject subclass. I used the Swift3 version.
E
Esqarrouth

Here is 2 ways I recommend doing it:

if let thisShape = aShape as? Square 

Or:

aShape.isKindOfClass(Square)

Here is a detailed example:

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

print( aShape is Square ), is operator is more preferable.
Good solution for me to get the type of objects.
I
ICL1901

Old question, but this works for my need (Swift 5.x):

print(type(of: myObjectName))

This is a copy of @Jérémy Lapointe's answer above (stackoverflow.com/a/40132792/764906)
P
Pranav Kasetti

Comment: I don't see how @JérémyLapointe answers the question. Using type(of:) only works by checking the compile-time information even if the actual type is a more specific subclass. There is now an easier way to dynamically query the type in Swift 5.1 without resorting to dynamicType like @Dash suggests. For more details on where I got this information, see SE-0068: Expanding Swift Self to class members and value types.

Code

Swift 5.1

// Within an instance method context
Self.self

// Within a static method context
self

This allows the use of Self as shorthand for referring to the containing type (in the case of structs, enums, and final class) or the dynamic type (in the case of non-final classes).

Explanation

The proposal explains well why this approach improves on dynamicType:

Introducing Self addresses the following issues: dynamicType remains an exception to Swift's lowercased keywords rule. This change eliminates a special case that's out of step with Swift's new standards. Self is shorter and clearer in its intent. It mirrors self, which refers to the current instance. It provides an easier way to access static members. As type names grow large, readability suffers. MyExtremelyLargeTypeName.staticMember is unwieldy to type and read. Code using hardwired type names is less portable than code that automatically knows its type. Renaming a type means updating any TypeName references in code. Using self.dynamicType fights against Swift's goals of concision and clarity in that it is both noisy and esoteric. Note that self.dynamicType.classMember and TypeName.classMember may not be synonyms in class types with non-final members.


j
josef

If you get an "always true/fails" warning you may need to cast to Any before using is

(foo as Any) is SomeClass

C
Christian Krueger

If a parameter is passed as Any to your function, you can test on a special type like so :

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

C
CPD

Depends on the use case. But let's assume you want to do something useful with your "variable" types. The Swift switch statement is very powerful and can help you get the results you're looking for...

    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
    }

In this case, have a simple dictionary that contains key/value pairs that can be UInt, Int or String. In the .filter() method on the dictionary, I need to make sure I test for the values correctly and only test for a String when it's a string, etc. The switch statement makes this simple and safe! By assigning 9 to the variable of type Any, it makes the switch for Int execute. Try changing it to:

   let eValue:Any = "home9"

..and try it again. This time it executes the as String case.


A
Avi Cohen
//: 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