Как узнать тип объекта (в Swift)?

255

Когда вы пытаетесь понять программу или в некоторых сложных случаях, полезно иметь возможность на самом деле узнать, что это за тип. Я знаю, что отладчик может показать вам некоторую информацию о типе, и вы обычно можете полагаться на вывод типа, чтобы не указывать тип в таких ситуациях, но, тем не менее, мне бы очень хотелось иметь что-то вроде Pythontype()

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

Тем не менее, это всего лишь небольшой шаг к фактическому обнаружению типа любого данного объекта.

редактирование : я удалил значительное количество теперь не относящихся к делу деталей - посмотрите историю изменений, если вам интересно :)

Jiaaro
источник
1
Возможный дубликат Как получить класс переменной в Swift?
Дэвид Берри
1
Интересно, что print(mc)или dump(mc)напечатает резюме (которое вы можете получить toString(mc)или reflect(mc).summary), которое будет содержать имя класса где-то там. Но не ясно, как получить только имя класса самостоятельно.
newacct
@ Давид похож, но не все переменные являются экземплярами классов. Кроме того, эти вопросы действительно касались проверки того, соответствует ли тип тому, что ищет программист, в то время как я надеюсь просто узнать тип опт
Jiaaro

Ответы:

285

Версия Swift 3:

type(of: yourObject)
Жереми Лапойнт
источник
8
Интересный факт. Это не работает с неявно развернутыми опциями! то есть var myVar: SomeType!. Компилятор выдает ошибку «Невозможно преобразовать значение типа« SomeType! .Type »(он же« ImplicitlyUnwrappedOptional <SomeType> .Type ») в тип ожидаемого аргумента« AnyClass »(он же« AnyObject.Type »). Компилятор предлагает добавить as! AnyClassпосле типа, но затем программа аварийно завершает работу с некоторым «EXC_BAD_INSTRUCTION» и другим jiberrish, который я не могу расшифровать
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!")
    }
Gudbergur
источник
7
Это круто. Помните, что если зеркальный объект имеет необязательный тип, то сравнение его с необязательным типом завершится неудачей. Stringи Optional(String)не одинаковы.
Томас Вербик
1
Именно то, что я искал, хотел узнать, какой тип объекта
Иосиф
Существует ли тип сравнения в этом контексте, который не потерпит неудачу при сравнении необязательных и не необязательных типов?
Крис Принс
Это то, что я искал. Спасибо @ Гудбергур.
Мубин Молл
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. Если вы работаете с Какао, вы можете просто использовать это свойство.

class MyObj: NSObject {
    init() {
        super.init()
        println("My class is \(self.className)")
    }
}
MyObj()
тир
источник
2
Эй, не уверен, когда это изменилось, но, как указал Алекс Прецлав, поведение изменилось.
Джиаро
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, я обновил свой ответ тем, что, как мне кажется, вы искали в своем первоначальном вопросе
Алекс Прецлав,
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>
}
Валерий Лидер
источник
Вторая часть вашего ответа неверна. Оператор 'if let' с as?условным приведением делает то же самое isKindOfClass, но также предоставляет результат преобразования, если оно выполнено успешно.
Авольф
Эквивалентом isMemberOfClassявляется условие object.dynamicType == ClassName.self.
Авольф
18

Свифт 3:

if unknownType is MyClass {
   //unknownType is of class type MyClass
}
Питер
источник
Я думаю, что isсуществует до Swift 3 ...?
Николас Миари
10

Для Swift 3.0

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

Для Swift 2.0 - 2.3

String(<Class-Name>)
techloverr
источник
1
Важно, чтобы этот ответ был правильным для меня, заключается в том, что результирующая строка точно соответствует имени класса - так что я могу использовать это для получения имени сущности Core Data из подкласса NSManagedObject. Я использовал версию Swift3.
Кендалл Хельмштеттер Гелнер
8

Вот 2 способа, которые я рекомендую сделать это:

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")
}
Esqarrouth
источник
2
print( aShape is Square ), isОператор является более предпочтительным.
DawnSong
Хорошее решение для меня, чтобы получить тип объектов.
nihasmata
1

Зависит от варианта использования. Но давайте предположим, что вы хотите сделать что-то полезное с вашими «переменными» типами. switchЗаявление Swift очень мощно и может помочь вам получить результаты, которые вы ищете ...

    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дело.

CPD
источник
1

Если вы получаете предупреждение «всегда верно / неуспешно», вам может потребоваться привести к Any перед использованием 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
Ави Коэн
источник