Jak znaleźć typ obiektu (w Swift)?

255

Kiedy próbujesz zrozumieć program lub w niektórych przypadkach narożnych, warto być w stanie dowiedzieć się, jakiego typu jest coś. Wiem, że debuger może wyświetlać pewne informacje o typie, i zwykle możesz polegać na wnioskowaniu o typie, aby uniknąć nieokreślania typu w takich sytuacjach, ale nadal naprawdę chciałbym mieć coś takiego jak Pythontype()

dynamicType (patrz to pytanie )

Aktualizacja: zostało to zmienione w najnowszej wersji Swift, obj.dynamicTypeteraz zawiera odniesienie do typu, a nie wystąpienia typu dynamicznego.

Ten wydaje się najbardziej obiecujący, ale do tej pory nie byłem w stanie znaleźć faktycznego typu

class MyClass {
    var count = 0
}

let mc = MyClass()

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

Próbowałem również za pomocą odniesienia do instancji klasy nowy obiekt, który robi pracę, ale dziwnie dał mi błąd mówiąc, muszę dodać requiredinicjator:

Pracuje:

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

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

Jednak wciąż jest to tylko mały krok w kierunku odkrycia rodzaju dowolnego obiektu

edycja : usunąłem znaczną liczbę nieistotnych już szczegółów - spójrz na historię edycji, jeśli jesteś zainteresowany :)

Jiaaro
źródło
1
możliwy duplikat Jak uzyskać klasę zmiennej w Swift?
David Berry
1
Co ciekawe, print(mc)czy dump(mc)będzie wydrukować podsumowanie (które można uzyskać z toString(mc)lub reflect(mc).summary), która będzie zawierać nazwę klasy gdzieś tam. Ale nie jest jasne, jak samemu zdobyć nazwę klasy.
newacct
@David podobne, ale nie wszystkie zmienne są instancjami klas. Również pytania dotyczyły naprawdę sprawdzenia, czy typ pasuje do tego, czego szuka programista, ale mam nadzieję, że po prostu znajdę typ hurtowo
Jiaaro

Odpowiedzi:

284

Wersja Swift 3:

type(of: yourObject)
Jérémy Lapointe
źródło
8
Śmieszny fakt. Nie działa to z niejawnie rozpakowanymi opcjami! tj var myVar: SomeType!. Kompilator wyświetla błąd „Nie można przekonwertować wartości typu„ SomeType! .Type ”(alias„ ImplicitlyUnwrappedOptional <SomeType> .Type ”) na oczekiwany typ argumentu„ AnyClass ”(alias„ AnyObject.Type ”). Kompilator sugeruje dodanie as! AnyClasspo typie, ale następnie program ulega awarii z pewnym „EXC_BAD_INSTRUCTION” i innym dżibrem, którego nie mogę rozszyfrować
LightningStryk
1
Rzeczywiście, powinna to być zaakceptowana odpowiedź teraz, gdy istnieje Swift 3. Dziękuję Jeremy!
biomiker
1
Jeśli szukasz konkretnej nazwy typu, gdy typem jest typ protokołu, może to nie działać.
Chris Prince
2
Jeśli masz taki, Stringktóry jest przekazywany jako typ, Anyto type(of:)wyświetli Any, a nie String.
ScottyBlades
1
@ScottyBlades, więc jakie będzie rozwiązanie. Czy możesz podać
Mubin Mall
109

Swift 2.0:

Właściwym sposobem na przeprowadzenie tego rodzaju introspekcji jest użycie struktury 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)

Następnie, aby uzyskać dostęp do samego typu ze Mirrorstruktury, użyj właściwości w następujący subjectTypesposób:

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

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

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

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

Następnie możesz użyć czegoś takiego:

    if anyMirror.subjectType == String.self {
        print("anyObject is a string!")
    } else {
        print("anyObject is not a string!")
    }
Gudbergur
źródło
7
To jest świetne. Uwaga: jeśli dublowany obiekt jest typu opcjonalnego, wówczas porównanie go z typem nie opcjonalnym zakończy się niepowodzeniem. Stringi Optional(String)nie są takie same.
Thomas Verbeek
1
Dokładnie to, czego szukałem, chciałem wiedzieć, jaki jest typ obiektu
Joseph
Czy istnieje w tym kontekście rodzaj porównania, który nie zawiedzie przy porównywaniu opcjonalnych z nie opcjonalnymi typami?
Chris Prince,
Właśnie tego szukałem. Dziękuję @Gudbergur.
Mubin Mall
60

dynamicType.printClassNameKod jest od przykładu w książce Swift. Nie ma możliwości, aby bezpośrednio pobrać niestandardową nazwę klasy, ale możesz sprawdzić typ instancji za pomocą issłowa kluczowego, jak pokazano poniżej. Ten przykład pokazuje również, jak zaimplementować niestandardową funkcję className, jeśli naprawdę chcesz, aby nazwa klasy była łańcuchem.

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

Uwaga:
te podklasy NSObjectjuż implementują własną funkcję className. Jeśli pracujesz z Cocoa, możesz po prostu użyć tej właściwości.

class MyObj: NSObject {
    init() {
        super.init()
        println("My class is \(self.className)")
    }
}
MyObj()
Dziarskość
źródło
2
Hej, nie jestem pewien, kiedy to się zmieniło, ale jak zauważył Alex Pretzlav, zachowanie się zmieniło.
Jiaaro
1
Tak. Począwszy od wersji Swift 3.0, subjectTypenie jest już dostępny i dynamicTypepowoduje komunikat przestarzałego kompilatora.
Raphael
41

Począwszy od Xcode 6.0.1 (przynajmniej nie jestem pewien, kiedy go dodali), twój oryginalny przykład działa teraz:

class MyClass {
    var count = 0
}

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

Aktualizacja:

Aby odpowiedzieć na oryginalne pytanie, możesz z powodzeniem używać środowiska wykonawczego Objective-C z prostymi obiektami Swift.

Spróbuj wykonać następujące czynności:

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))
Alex Pretzlav
źródło
Wygląda na to, że zmienili go, aby podać typ zamiast wystąpienia.
Jiaaro
@Jiaaro, zaktualizowałem swoją odpowiedź tym, co myślę, że szukałeś w swoim pierwotnym pytaniu
Alex Pretzlav
36

Jeśli musisz po prostu sprawdzić, czy zmienna jest typu X lub czy jest zgodna z jakimś protokołem, możesz użyć islub as?jak poniżej:

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

Jest to równoważne z isKindOfClass Obj-C.

I to jest równoważne z conformsToProtocollubisMemberOfClass

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>
}
Valerii Lider
źródło
Druga część twojej odpowiedzi jest błędna. Instrukcja „if let” z as?rzutowaniem warunkowym działa isKindOfClassrównież tak samo, tylko zapewnia wynik rzutowania, jeśli zakończy się powodzeniem.
awolf
Odpowiednikiem isMemberOfClassjest warunek object.dynamicType == ClassName.self.
awolf,
18

Swift 3:

if unknownType is MyClass {
   //unknownType is of class type MyClass
}
Piotr
źródło
Myślę, że isistnieje przed Swift 3 ...?
Nicolas Miari
10

Dla Swift 3.0

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

Dla Swift 2.0 - 2.3

String(<Class-Name>)
techloverr
źródło
1
Ważną rzeczą w tym, że ta odpowiedź jest dla mnie poprawna, jest to, że wynikowy łańcuch dokładnie pasuje do nazwy klasy - dzięki czemu mogę użyć tego, aby uzyskać nazwę encji Core Data z podklasy NSManagedObject. Użyłem wersji Swift3.
Kendall Helmstetter Gelner
8

Oto dwa sposoby, które polecam to zrobić:

if let thisShape = aShape as? Square 

Lub:

aShape.isKindOfClass(Square)

Oto szczegółowy przykład:

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")
}
Esqarrouth
źródło
2
print( aShape is Square ), isoperator jest bardziej preferowany.
DawnSong,
Dobre rozwiązanie dla mnie, aby uzyskać rodzaj obiektów.
nihasmata
1

Zależy od przypadku użycia. Załóżmy jednak, że chcesz zrobić coś użytecznego z typami „zmiennymi”. switchOświadczenie Swift jest bardzo potężne i może pomóc uzyskać wyniki, których szukasz ...

    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
    }

W takim przypadku należy mieć prosty słownik, który zawiera pary klucz / wartość, które mogą być UInt, Int lub String. W .filter()metodzie w słowniku muszę się upewnić, że poprawnie testuję wartości i testuję tylko ciąg znaków, gdy jest to ciąg znaków itp. Instrukcja switch sprawia, że ​​jest to proste i bezpieczne! Przypisując 9 do zmiennej typu Any, powoduje przełączenie na Int. Spróbuj zmienić na:

   let eValue:Any = "home9"

... i spróbuj ponownie. Tym razem wykonuje as Stringsprawę.

CPD
źródło
1

Jeśli pojawi się ostrzeżenie „zawsze prawdziwe / nieudane”, może być konieczne użycie opcji Any przed użyciem is

(foo as Any) is SomeClass
Joseph
źródło
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
Avi Cohen
źródło