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


при попытке понять программу, или в некоторых угловых случаях, полезно иметь возможность на самом деле выяснить, какой тип что-то есть. Я знаю, что отладчик может показать вам некоторую информацию о типе, и вы обычно можете полагаться на вывод типа, чтобы уйти от указания типа в этих ситуациях, но все же мне очень хотелось бы иметь что-то вроде 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()

все еще только a небольшой шаг к фактическому обнаружению типа любого данного объекта, хотя

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

11 215

11 ответов:

Swift 3 версия:

type(of: yourObject)

в 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 struct вы бы использовали свойство 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!")
    }

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

начиная с Xcode 6.0.1 (по крайней мере, не уверен, когда они добавили его), ваш исходный пример теперь работает:

class MyClass {
    var count = 0
}

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

обновление:

чтобы ответить на исходный вопрос, вы можете успешно использовать среду выполнения Obj-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))

Если вам просто нужно проверить, является ли переменная типа 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>
}

это работает в swift 3

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

вот 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")
}

Для Swift 3.0

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

Для Swift 2.0 - 2.3

String(<Class-Name>)

зависит от варианта использования. Но давайте предположим, что вы хотите сделать что-то полезное с вашими "переменными" типами. Свифт 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 =  == testString
        case let testUInt as UInt:
            r =  == testUInt
        case let testInt as Int:
            r =  == testInt
        default:
            r = false
        }

        return r &&  == eIndex
    }

в этом случае есть простой словарь, который содержит пары ключ/значение, которые могут быть UInt, Int или String. В .filter() метод в словаре, мне нужно убедиться, что я правильно тестирую значения и только тестирую строку, когда это строка и т. д. Этот оператор switch делает это простым и безопасным! Назначая 9 переменной типа Any, он делает переключатель для int execute. Попробуйте изменить его на:

   let eValue:Any = "home9"

..и попробуй еще раз. На этот раз он выполняет as String случае.

Если вы получаете предупреждение "всегда true/fails", вам может потребоваться привести к любому перед использованием is

(foo as Any) is SomeClass
//: 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