Как распечатать тип или класс переменной в Swift?

есть ли способ распечатать тип выполнения переменной в swift? Например:

var now = NSDate()
var soon = now.dateByAddingTimeInterval(5.0)

println("(now.dynamicType)") 
// Prints "(Metatype)"

println("(now.dynamicType.description()")
// Prints "__NSDate" since objective-c Class objects have a "description" selector

println("(soon.dynamicType.description()")
// Compile-time error since ImplicitlyUnwrappedOptional<NSDate> has no "description" method

В приведенном выше примере я ищу способ показать, что переменная "скоро" имеет тип ImplicitlyUnwrappedOptional<NSDate> или хотя бы NSDate!.

30 ответов


Обновление Сентябрь 2016

Swift 3.0: Использовать type(of:), например,type(of: someThing) (поскольку dynamicType ключевое слово было удалено)

Обновление Октябрь 2015:

я обновил приведенные ниже примеры до нового синтаксиса Swift 2.0 (например,println был заменен print, toString() теперь String()).

из Xcode 6.3 примечания к выпуску:

@nschum указывает в комментариях, что в Xcode 6.3 примечания к выпуску показать по-другому:

значения типа теперь печатаются как полное имя типа demangled при использовании с интерполяция println или строки.

import Foundation

class PureSwiftClass { }

var myvar0 = NSString() // Objective-C class
var myvar1 = PureSwiftClass()
var myvar2 = 42
var myvar3 = "Hans"

print( "String(myvar0.dynamicType) -> \(myvar0.dynamicType)")
print( "String(myvar1.dynamicType) -> \(myvar1.dynamicType)")
print( "String(myvar2.dynamicType) -> \(myvar2.dynamicType)")
print( "String(myvar3.dynamicType) -> \(myvar3.dynamicType)")

print( "String(Int.self)           -> \(Int.self)")
print( "String((Int?).self         -> \((Int?).self)")
print( "String(NSString.self)      -> \(NSString.self)")
print( "String(Array<String>.self) -> \(Array<String>.self)")

выходы:

String(myvar0.dynamicType) -> __NSCFConstantString
String(myvar1.dynamicType) -> PureSwiftClass
String(myvar2.dynamicType) -> Int
String(myvar3.dynamicType) -> String
String(Int.self)           -> Int
String((Int?).self         -> Optional<Int>
String(NSString.self)      -> NSString
String(Array<String>.self) -> Array<String>

обновление для Xcode 6.3:

можно использовать _stdlib_getDemangledTypeName():

print( "TypeName0 = \(_stdlib_getDemangledTypeName(myvar0))")
print( "TypeName1 = \(_stdlib_getDemangledTypeName(myvar1))")
print( "TypeName2 = \(_stdlib_getDemangledTypeName(myvar2))")
print( "TypeName3 = \(_stdlib_getDemangledTypeName(myvar3))")

и сделать это как выход:

TypeName0 = NSString
TypeName1 = __lldb_expr_26.PureSwiftClass
TypeName2 = Swift.Int
TypeName3 = Swift.String

Оригинал ответ:

до Xcode 6.3 _stdlib_getTypeName есть искаженное имя типа переменной. запись в блоге Эвана Свика помогает расшифровать эти строки:

например _TtSi расшифровывается как внутреннийInt тип.

у Майка Эша есть отличная запись в блоге, охватывающая ту же тему.


Edit:новая toString функции был введен в Swift 1.2 (Xcode 6.3).

теперь вы можете распечатать тип demangled любого типа с помощью .self и любой экземпляр, используя .dynamicType:

struct Box<T> {}

toString("foo".dynamicType)            // Swift.String
toString([1, 23, 456].dynamicType)     // Swift.Array<Swift.Int>
toString((7 as NSNumber).dynamicType)  // __NSCFNumber

toString((Bool?).self)                 // Swift.Optional<Swift.Bool>
toString(Box<SinkOf<Character>>.self)  // __lldb_expr_1.Box<Swift.SinkOf<Swift.Character>>
toString(NSStream.self)                // NSStream

позвонить YourClass.self и yourObject.dynamicType.

ссылка:https://devforums.apple.com/thread/227425.


это то, что вы ищете?

println("\(object_getClassName(now))");

он печатает "__NSDate"

обновление: обратите внимание, что это больше не работает с Beta05


Swift 3.0

let string = "Hello"
let stringArray = ["one", "two"]
let dictionary = ["key": 2]

print(type(of: string)) // "String"

// Get type name as a string
String(describing: type(of: string)) // "String"
String(describing: type(of: stringArray)) // "Array<String>"
String(describing: type(of: dictionary)) // "Dictionary<String, Int>"

// Get full type as a string
String(reflecting: type(of: string)) // "Swift.String"
String(reflecting: type(of: stringArray)) // "Swift.Array<Swift.String>"
String(reflecting: type(of: dictionary)) // "Swift.Dictionary<Swift.String, Swift.Int>"

мой текущий Xcode-версия 6.0 (6A280e).

import Foundation

class Person { var name: String; init(name: String) { self.name = name }}
class Patient: Person {}
class Doctor: Person {}

var variables:[Any] = [
    5,
    7.5,
    true,
    "maple",
    Person(name:"Sarah"),
    Patient(name:"Pat"),
    Doctor(name:"Sandy")
]

for variable in variables {
    let typeLongName = _stdlib_getDemangledTypeName(variable)
    let tokens = split(typeLongName, {  == "." })
    if let typeName = tokens.last {
        println("Variable \(variable) is of Type \(typeName).")
    }
}

выход:

Variable 5 is of Type Int.
Variable 7.5 is of Type Double.
Variable true is of Type Bool.
Variable maple is of Type String.
Variable Swift001.Person is of Type Person.
Variable Swift001.Patient is of Type Patient.
Variable Swift001.Doctor is of Type Doctor.

начиная с Xcode 6.3 с Swift 1.2, вы можете просто конвертировать значения типов в полный demangled String.

toString(Int)                   // "Swift.Int"
toString(Int.Type)              // "Swift.Int.Type"
toString((10).dynamicType)      // "Swift.Int"
println(Bool.self)              // "Swift.Bool"
println([UTF8].self)            // "Swift.Array<Swift.UTF8>"
println((Int, String).self)     // "(Swift.Int, Swift.String)"
println((String?()).dynamicType)// "Swift.Optional<Swift.String>"
println(NSDate)                 // "NSDate"
println(NSDate.Type)            // "NSDate.Type"
println(WKWebView)              // "WKWebView"
toString(MyClass)               // "[Module Name].MyClass"
toString(MyClass().dynamicType) // "[Module Name].MyClass"

вы все еще можете получить доступ к классу, через className (который возвращает String).

на самом деле существует несколько способов получить класс, например classForArchiver, classForCoder, classForKeyedArchiver (все вернуть AnyClass!).

вы не можете получить тип примитива (примитив не класс).

пример:

var ivar = [:]
ivar.className // __NSDictionaryI

var i = 1
i.className // error: 'Int' does not have a member named 'className'

если вы хотите получить тип примитива, вы должны использовать bridgeToObjectiveC(). Пример:

var i = 1
i.bridgeToObjectiveC().className // __NSCFNumber

вы можете использовать reflect для получения информации об объекте.
Например, имя класса объекта:

var classname = reflect(now).summary

Мне повезло с:

let className = NSStringFromClass(obj.dynamicType)

Xcode 8 Swift 3.0 тип использования (of:)

let className = "\(type(of: instance))"

В Xcode 8, Swift 3.0

действия:

1. Получить тип:

Вариант 1:

let type : Type = MyClass.self  //Determines Type from Class

Вариант 2:

let type : Type = type(of:self) //Determines Type from self

2. Преобразовать тип в строку:

let string : String = "\(type)" //String

в Swift 3.0, вы можете использовать type(of:), as dynamicType ключевое слово было удалено.


SWIFT 3

С последней версией Swift 3 мы можем получить довольно описания имен типов через String инициализатор. Как, например,print(String(describing: type(of: object))). Где object может быть переменной экземпляра как массив, словарь,Int, a NSDate, экземпляр пользовательского класса, и т. д.

вот мой полный ответ: получить имя класса объекта в виде строки в Swift

что вопрос ищет способ получить имя класса объекта в виде строки, но также я предложил другой способ получить имя класса переменной, которая не является подклассом NSObject. Вот это:

class Utility{
    class func classNameAsString(obj: Any) -> String {
        //prints more readable results for dictionaries, arrays, Int, etc
        return String(describing: type(of: obj))
    }
}

я сделал статическую функцию, которая принимает в качестве параметра объект типа Any и возвращает свое имя класса как String :) .

я протестировал эту функцию с некоторыми переменными, такими как:

    let diccionary: [String: CGFloat] = [:]
    let array: [Int] = []
    let numInt = 9
    let numFloat: CGFloat = 3.0
    let numDouble: Double = 1.0
    let classOne = ClassOne()
    let classTwo: ClassTwo? = ClassTwo()
    let now = NSDate()
    let lbl = UILabel()

и выход было:

  • diccionary имеет тип словарь
  • массив имеет тип Array
  • numInt имеет тип Int
  • numFloat имеет тип CGFloat
  • numDouble имеет тип Double
  • classOne имеет тип: ClassOne
  • classTwo типа: ClassTwo
  • теперь имеет тип: дата
  • lbl имеет тип: UILabel

при использовании какао (не CocoaTouch), вы можете использовать className свойство для объектов, которые являются подклассами NSObject.

println(now.className)

это свойство недоступно для обычных объектов Swift, которые не являются подклассами NSObject (и на самом деле в Swift нет корневого идентификатора или типа объекта).

class Person {
    var name: String?
}

var p = Person()
println(person.className) // <- Compiler error

в CocoaTouch в это время нет способа получить строковое описание типа заданной переменной. Аналогичные функции также не существует для первобытного вводит какао или CocoaTouch.

Swift REPL может распечатать сводку значений, включая его тип, поэтому возможно, что этот способ самоанализа будет возможен через API в будущем.

редактировать: dump(object) кажется, сделать трюк.


Я пробовал некоторые другие ответы здесь, но пробег похоже очень на то, что объект является подчиненным.

однако я нашел способ получить имя класса Object-C для объекта, выполнив следующие действия:

now?.superclass as AnyObject! //replace now with the object you are trying to get the class name for

вот и пример того, как можно использовать это:

let now = NSDate()
println("what is this = \(now?.superclass as AnyObject!)")

в этом случае он будет печатать NSDate в консоли.


Я нашел это решение, которое, надеюсь, может работать на кого-то. Я создал метод класса для доступа к значению. Имейте в виду, что это будет работать только для подкласса NSObject. Но, по крайней мере, это чистое и аккуратное решение.

class var className: String!{
    let classString : String = NSStringFromClass(self.classForCoder())
    return classString.componentsSeparatedByString(".").last;
}

в последнем XCode 6.3 с Swift 1.2 это единственный способ, который я нашел:

if view.classForCoder.description() == "UISegment" {
    ...
}

в lldb начиная с beta 5 Вы можете увидеть класс объекта с помощью команды:

fr v -d r shipDate

, который выводит что-то вроде:

(DBSalesOrderShipDate_DBSalesOrderShipDate_ *) shipDate = 0x7f859940

команда расширена означает что-то вроде:

Frame Variable (печать переменной кадра) -d run_target (расширение динамического типа)

полезно знать, что использование "переменной фрейма" для вывода значений переменных гарантирует, что код не выполняется.


Я нашел решение для саморазвивающихся классов (или у вас есть доступ к).

место следующее вычисляемое свойство в пределах вашего определения класса объектов:

var className: String? {
    return __FILE__.lastPathComponent.stringByDeletingPathExtension
}

Теперь вы можете просто назвать имя класса для объекта, например:

myObject.className

обратите внимание, что это будет работает только если ваше определение класса в файле с именем ровно как класс, который вы хотите имя.

как это обычно по делу приведенный выше ответ должен сделать это для большинства случаев. Но в какой-то в особых случаях вам может потребоваться найти другое решение.


Если вам нужно имя класса внутри самого класса (файла) вы можете просто использовать эту строку:

let className = __FILE__.lastPathComponent.stringByDeletingPathExtension

возможно, этот метод помогает некоторым людям.


основываясь на ответах и комментариях, данных классом и Кевином Баллардом выше, я бы пошел с:

println(_stdlib_getDemangledTypeName(now).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon?).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon!).componentsSeparatedByString(".").last!)

println(_stdlib_getDemangledTypeName(myvar0).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar1).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar2).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar3).componentsSeparatedByString(".").last!)

который выводит:

"NSDate"
"ImplicitlyUnwrappedOptional"
"Optional"
"NSDate"

"NSString"
"PureSwiftClass"
"Int"
"Double"

let i: Int = 20


  func getTypeName(v: Any) -> String {
    let fullName = _stdlib_demangleName(_stdlib_getTypeName(i))
    if let range = fullName.rangeOfString(".") {
        return fullName.substringFromIndex(range.endIndex)
    }
    return fullName
}

println("Var type is \(getTypeName(i)) = \(i)")

многие из ответов здесь не работают с последним Swift (Xcode 7.1.1 на момент написания).

текущий способ получения информации заключается в создании Mirror и допросить, что. Для класса это так же просто, как:

let mirror = Mirror(reflecting: instanceToInspect)
let classname:String = mirror.description

дополнительная информация об объекте также может быть получена из Mirror. См.http://swiftdoc.org/v2.1/type/Mirror/ для деталей.


в верхнем ответе нет рабочего примера нового способа сделать это с помощью type(of:. Чтобы помочь новичкам вроде меня, вот рабочий пример, взятый в основном из документов Apple здесь -https://developer.apple.com/documentation/swift/2885064-type

doubleNum = 30.1

func printInfo(_ value: Any) {
    let varType = type(of: value)
    print("'\(value)' of type '\(varType)'")
}

printInfo(doubleNum)
//'30.1' of type 'Double'

похоже, что нет общего способа распечатать имя типа типа произвольного значения. Как отмечали другие, для класс экземпляры, которые вы можете распечатать value.className но для примитивных значений кажется, что во время выполнения информация о типе отсутствует.

например, похоже, что нет способа ввести:1.something() и выйти Int для любого значения something. (Вы можете, как предложил другой ответ, использовать i.bridgeToObjectiveC().className чтобы дать вам намек, но __NSCFNumber is не на самом деле типа i -- только то, что он будет преобразован, когда он пересекает границу объективной-C-функции.)

Я был бы рад оказаться ошибочным, но похоже, что проверка типов выполняется во время компиляции, и, как и C++ (с отключенным RTTI), большая часть информации о типе исчезла во время выполнения.


не совсем то, что вам нужно, но вы также можете проверить тип переменной против Swift-типов, таких как:

let object: AnyObject = 1

if object is Int {
}
else if object is String {
}

например.


Xcode 7.3.1, Swift 2.2:

String(instanceToPrint.self).componentsSeparatedByString(".").last


Swift 3.0, Xcode 8

со следующим кодом вы можете запросить экземпляр для его класса. Вы также можете сравнить два экземпляра, имеющих один и тот же класс.

// CREATE pure SWIFT class
class MySwiftClass {
    var someString : String = "default"
    var someInt    : Int = 5
}

// CREATE instances
let firstInstance = MySwiftClass()
let secondInstance = MySwiftClass()
secondInstance.someString = "Donald"
secondInstance.someInt = 24

// INSPECT instances
if type(of: firstInstance) === MySwiftClass.self {
    print("SUCCESS with ===")
} else {
    print("PROBLEM with ===")
}

if type(of: firstInstance) == MySwiftClass.self {
    print("SUCCESS with ==")
} else {
    print("PROBLEM with ==")
}

// COMPARE CLASS OF TWO INSTANCES
if type(of: firstInstance) === type(of: secondInstance) {
    print("instances have equal class")
} else {
    print("instances have NOT equal class")
}

Swift версия 4:

print("\(type(of: self)) ,\(#function)")
// within a function of a class

Спасибо @Joshua Dance


Это, как вы получаете строку типа объект или тип что это последовательный и учитывает которому модуль определение объекта принадлежит или вложенными. Работает в Swift 4.x.

@inline(__always) func typeString(for _type: Any.Type) -> String {
    return String(reflecting: type(of: _type))
}

@inline(__always) func typeString(for object: Any) -> String {
    return String(reflecting: type(of: type(of: object)))
}

struct Lol {
    struct Kek {}
}

// if you run this in playground the results will be something like
typeString(for: Lol.self)    //    __lldb_expr_74.Lol.Type
typeString(for: Lol())       //    __lldb_expr_74.Lol.Type
typeString(for: Lol.Kek.self)//    __lldb_expr_74.Lol.Kek.Type
typeString(for: Lol.Kek())   //    __lldb_expr_74.Lol.Kek.Type

это также удобно при проверке, является ли объект типом класса:

if someObject is SomeClass {
    //someObject is a type of SomeClass
}