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


335

Есть ли способ напечатать тип переменной во время выполнения в 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!.


43
@JasonMArcher Скажите, как это дубликат, если вопрос, который вы связали, был задан через 4 дня после этого?
Акашивский

Существует ряд вопросов о тестировании типа объекта Swift или чтении типа объекта Swift. Мы просто находим лучшие вопросы для использования в качестве «основных» вопросов по этому предмету. Предложенный дубликат имеет гораздо более тщательный ответ. Это не значит, что вы сделали что-то не так, просто мы пытаемся уменьшить беспорядок.
JasonMArcher

4
Предлагаемый дубликат не отвечает на тот же вопрос; Type.self не может быть выведен на консоль для целей отладки, он предназначен для передачи другим функциям, которые принимают Types как объекты.
Мэтт Бриджес

2
OT: Очень странно, что Swift не предлагает этого из коробки, и нужно возиться с такими низкоуровневыми библиотеками C. Стоит ли сообщать об ошибке?
qwerty_so

Ребята, я предоставил свой ответ ниже. Пожалуйста, посмотрите и дайте мне знать, если это то, что ожидается.
ДИЛИП КОСУРИ

Ответы:


377

Обновление сентябрь 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 показывают другой путь:

Значения типов теперь печатаются как полное имя разомкнутого типа при использовании с 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получил искаженное имя типа переменной. Запись в блоге Ewan Swick помогает расшифровать эти строки:

Например, _TtSiобозначает внутренний Intтип Swift .

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


2
Как вы нашли _stdlib_getTypeName()? Интегрированный REPL больше не существует, swift-ide-testне существует, и печать Xcode модуля Swift _исключает значения с префиксами.
Лили Баллард

10
Похоже, я могу lldbлегко найти символы в репле. Там же _stdlib_getDemangledTypeName()и _stdlib_demangleName().
Лили Баллард

1
О, аккуратно, я не осознавал, что это imageбыл ярлык target modules. Я закончил тем target modules lookup -r -n _stdlib_, что использовал , что, конечно, лучше выражено какimage lookup -r -n _stdlib_ libswiftCore.dylib
Лили Баллард

1
К вашему сведению - похоже, что toString (...) был заменен на String (...).
Ник

6
Swift 3.0: dynamicTypeключевое слово было удалено из Swift. Вместо type(of:)него была добавлена новая примитивная функция : github.com/apple/swift/blob/master/CHANGELOG.md
Сю

46

Редактировать: новая 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 .


2
Я ответил на эту тему, но я добавлю свой комментарий здесь. Эти значения имеют тип «Метатип», но не представляется простым способом выяснить, какой тип представляет объект Метатип, и именно это я и ищу.
Мэтт Бриджес

Они имеют тип Metatype и являются классами. Они представляют класс. Не уверен точно, в чем твоя проблема с этим. someInstance.dynamicType.printClassName()напечатает название класса.
Аналоговый файл

18
метод printClassName () - это просто пример метода, который они создали в одном из примеров в документации, это не вызов API, к которому вы можете получить доступ.
Дэйв Вуд

4
К вашему сведению - похоже, что toString (...) был заменен на String (...)
Ник

33

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

1
Реализация Swift 2 особенно хороша в случаях enum. Кто-нибудь знает, документировано ли это / гарантировано Apple? Стандартная библиотека просто содержит комментарий, что это подходит для отладки ...
milos

30

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

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

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

ОБНОВЛЕНИЕ : Пожалуйста, обратите внимание, что это больше не работает с Beta05


2
Не работает для быстрых классов. Возвращает «UnsafePointer (0x7FBB18D06DE0)»
aryaxt

4
Также не работает для классов ObjC (по крайней мере, в Beta5), для NSManagedObject я просто получаю "Builtin.RawPointer = address"
Кендалл Хелмштеттер Гелнер

23

Мой текущий 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, { $0 == "." })
    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.

1
Хорошо, но вопрос также включал Optionals, вывод дополнительных параметров задан Optional(...), поэтому ваш код будет возвращаться только Optionalкак Type. Он также должен отображаться var optionalTestVar: Person? = Person(name:"Sarah")как Необязательный (Person) и var с Person! as ImplicitlyUnwrappedOptional (Person)
бинарная версия

18

Начиная с Xcode 6.3 с Swift 1.2, вы можете просто конвертировать значения типов в полностью разделенные 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"

Возможно ли другое направление в 1.2? Инициализировать экземпляр на основе имени класса?
user965972

@ user965972 Насколько я знаю, я не думаю, что есть.
Кайдинг

17

Вы все еще можете получить доступ к классу через className(который возвращает a 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

Могу поспорить, это примитив. К сожалению, вы не можете получить тип примитива.
Леандрос

Эти примеры, кажется, не работают на детской площадке. Я получаю ошибку как для примитивов, так и для не примитивов.
Мэтт Бриджес

попытаться import Cocoa.
Леандрос

9
className представляется доступным только для объектов NSObject при разработке для OSX; это не доступно в iOS SDK ни для NSObjects, ни для «обычных» объектов
Мэтт Бриджес,

3
classNameявляется частью поддержки AppleScript Какао и определенно не должен использоваться для общего самоанализа. Очевидно, что это не доступно на iOS.
Николай Рюэ

16

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

var classname = отражает (сейчас).

В бета-версии 6 я получил название приложения + имя класса в слегка искаженном виде, но это только начало. +1 за reflect(x).summary- спасибо!
Оли

не работает, печатает пустую строку в Swift 1.2 Xcode 6.4 и iOs 8.4
Хуан Боеро

13

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

let className = NSStringFromClass(obj.dynamicType)

@hdost чувак, ничего не работает, но classForCoder работает.
Satheeshwaran


10

В 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


7

SWIFT 5

С последней версией 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()

и вывод был:

  • словарь имеет тип словарь
  • массив имеет тип Array
  • numInt имеет тип Int
  • numFloat относится к типу CGFloat
  • numDouble типа Double
  • classOne имеет тип: ClassOne
  • classTwo имеет тип: ClassTwo
  • сейчас имеет тип: Дата
  • фунт имеет тип: UILabel

5

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

println(now.className)

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

class Person {
    var name: String?
}

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

В CocoaTouch в настоящее время нет способа получить строковое описание типа данной переменной. Подобной функциональности также не существует для примитивных типов ни в Cocoa, ни в CocoaTouch.

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

РЕДАКТИРОВАТЬ : dump(object)кажется, делает свое дело.


Даже в Xcode 10 и Swift 4.2 dump(object)он действительно мощный. Спасибо.
Алекс Заватоне

5

Верхний ответ не имеет рабочего примера нового способа сделать это с помощью 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'

4

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

Однако я нашел способ получить имя класса 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 в консоли.


4

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

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

4

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

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

ПРИМЕЧАНИЕ: description () возвращает имя класса в формате <swift module name>.<actual class name>, поэтому вы хотите разделить строку с помощью a .и получить последний токен.
Мэтью Куирос

4

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

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

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

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


Что не так с ответами stackoverflow.com/a/25345480/1187415 и stackoverflow.com/a/32087449/1187415 ? Похоже, что оба хорошо работают со Swift 2. (Остальные не проверялись.)
Martin R

Мне нравится это как общее решение, но получение «Зеркало для ViewController» (постороннее «Зеркало для») - подождите, похоже, использование «.subjectType» делает свое дело!
Дэвид Х

3

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

fr v -d r shipDate

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

(DBSalesOrderShipDate_DBSalesOrderShipDate_ *) shipDate = 0x7f859940

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

Frame Variable(вывести переменную кадра) -d run_target(развернуть динамические типы)

Полезно знать, что использование «Frame Variable» для вывода значений переменных гарантирует, что код не будет выполнен.


3

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

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

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

Теперь вы можете просто вызвать имя класса для вашего объекта следующим образом:

myObject.className

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

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


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

let className = __FILE__.lastPathComponent.stringByDeletingPathExtension

Может быть, этот метод помогает некоторым людям там.


3

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

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"

Я думаю, что это лучший ответ.
Maschall

Я закончил тем, что использовал в Swift 1.2 следующее: toString(self.dynamicType).componentsSeparatedByString(".").last!очевидно, я был в контексте объекта и мог ссылаться self.
Джо

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

3

Swift версия 4:

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

Спасибо @ Джошуа Дэнс


3

Свифт 4:

// "TypeName"
func stringType(of some: Any) -> String {
    let string = (some is Any.Type) ? String(describing: some) : String(describing: type(of: some))
    return string
}

// "ModuleName.TypeName"
func fullStringType(of some: Any) -> String {
    let string = (some is Any.Type) ? String(reflecting: some) : String(reflecting: type(of: some))
    return string
}

Использование:

print(stringType(of: SomeClass()))     // "SomeClass"
print(stringType(of: SomeClass.self))  // "SomeClass"
print(stringType(of: String()))        // "String"
print(fullStringType(of: String()))    // "Swift.String"

2

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

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

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


Можете ли вы привести пример использования className для печати «NSDate» для переменной «сейчас» в моем примере, который работает на площадке Xcode6? Насколько я могу судить, className не определено в объектах Swift, даже тех, которые являются подклассами NSObject.
Мэтт Бриджес

Похоже, что 'className' доступно для объектов, которые происходят от NSObject, но только при разработке для Mac, а не для iOS. Похоже, что для iOS вообще нет обходного пути.
Мэтт Бриджес

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

Мне любопытно, как REPL может печатать типы значений.
Мэтт Бриджес

REPL почти наверняка интерпретируется и не компилируется, что легко объяснило бы это
ipmcc

2

Вот как вы получаете строку типа вашего объекта или типа, которая является согласованной и учитывает, к какому модулю принадлежит определение объекта или в который он вложен. Работает в 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

2

Чтобы получить тип объекта или класс объекта в Swift , вам необходимо использовать тип (of: yourObject)

тип (из: вашОбъект)


1

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

let object: AnyObject = 1

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

Например.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.