Як дізнатися тип об’єкта (у Swift)?


255

Намагаючись зрозуміти програму або в деяких кутових випадках, корисно мати можливість фактично з’ясувати, що таке щось. Я знаю, що налагоджувач може показати вам інформацію про тип, і ви, як правило, можете покластися на висновок типу, щоб уникнути, не вказуючи тип у цих ситуаціях, але все-таки я дуже хотів би мати щось на зразок Python'stype()

dinamiльний тип (див. це питання )

Оновлення: це було змінено в останній версії 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

@David подібний, але не всі змінні є екземплярами класу. Крім того, питання справді
стосувались


Відповіді:


284

Версія Swift 3:

type(of: yourObject)

8
Смішний факт. Це не працює з неявно розгорнутою опцією! тобто var myVar: SomeType!. Компілятор видає помилку "Неможливо перетворити значення типу" SomeType! .Type "(він же" ImplicitlyUnwrappedOptions <SomeType> .Type ") до очікуваного типу аргументу" AnyClass "(він же" AnyObject.Type "). Компілятор пропонує додавати as! AnyClassпісля типу, але потім програма виходить з ладу з деяким "EXC_BAD_INSTRUCTION" та іншим джибриром, який я не можу розшифрувати.
LightningStryk

1
Дійсно, це має бути прийнятою відповіддю тепер, коли Swift 3 існує. Дякую Джеремі!
біомікер

1
Якщо ви шукаєте назву конкретного типу, коли тип має тип протоколу, це може не працювати для вас.
Кріс Принс

2
Якщо у вас є Stringтип, який передається як тип, Anyтоді type(of:)буде виводитися Any, а не String.
ScottyBlades

1
@ScottyBlades, що таке рішення. Ви можете надати?
Mubin Mall

109

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

7
Це чудово. Остерігайтеся, що якщо дзеркальний об'єкт має необов'язковий тип, то порівняння його з необов'язковим типом не вдасться. Stringі Optional(String)не однакові.
Томас Вербек

1
Саме те, що я шукав, хотів дізнатися, який тип об’єкта
Йосип

Чи є в цьому контексті порівняння типу, яке не вийде з ладу при порівнянні необов'язкових із необов'язковими типами?
Кріс Принс

Ось що я шукав. Дякую @Gudbergur
Mubin Mall

60

dynamicType.printClassNameКод з прикладу в книзі Свіфта. Я не знаю, як безпосередньо схопити назву спеціального класу, але ви можете перевірити тип екземплярів, використовуючи 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. Якщо ви працюєте з Какао, ви можете просто скористатися цією властивістю.

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

2
Гей, не впевнений, коли він змінився, але, як зазначив Олексій Прецлав, поведінка змінилася.
Jiaaro

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

Схоже, вони змінили його, щоб надати вам тип, а не екземпляр.
Jiaaro

@Jiaaro, я оновив свою відповідь тим, що, на мою думку, ви шукали у своєму первісному запитанні
Алекс Прецлав

36

Якщо вам просто потрібно перевірити, чи є змінна типу X, чи вона відповідає деякому протоколу, ви можете використовувати isабо as?як у наступному:

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

Це еквівалентно isKindOfClassв Obj-C.

І це еквівалентно conformsToProtocol, або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>
}

Друга частина вашої відповіді неправильна. Висловлювання "якщо нехай" із as?умовним амплітудою робить те саме, isKindOfClassщо і, просто також надає результат виступу в разі успіху.
awolf

Еквівалент isMemberOfClass- умова object.dynamicType == ClassName.self.
awolf


10

Для Swift 3.0

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

Для Swift 2.0 - 2.3

String(<Class-Name>)

1
Важливим, що ця відповідь є правильною для мене, це те, що отриманий рядок точно відповідає імені класу - тому я можу використовувати це, щоб отримати ім'я суб'єкта Core Data від підкласу 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оператор є більш кращим.
СвітанокСонг

Гарне рішення для мене, щоб отримати тип об’єктів.
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()методі зі словника мені потрібно переконатися, що я перевіряю ціннісні значення правильно, і тестуватимуть лише для String, коли це рядок тощо. Присвоївши 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.