Есть ли способ распечатать тип среды выполнения в 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!
.
Обновление сентябрь 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
получил измененное имя переменной. Запись в блоге Ewan Swick помогает расшифровать эти строки:
например. _TtSi
обозначает внутренний тип Swift Int
.
У Майка Эша отличная запись в блоге, посвященная той же теме.
_stdlib_getTypeName()
? Встроенный REPL больше не существует, swift-ide-test
также не существует, и печать Xcode модуля Swift исключает _
префиксные значения.
lldb
в ответе. Также есть _stdlib_getDemangledTypeName()
и _stdlib_demangleName()
.
Изменить: Новая функция 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
.
someInstance.dynamicType.printClassName()
напечатает имя класса.
Это то, что вы ищете?
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, { $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.
Optional(...)
, поэтому ваш код будет возвращать только Optional
в качестве Type. Также должно отображаться var optionalTestVar: Person? = Person(name:"Sarah")
как Необязательный (Персона) и Вар с Персоном! as ImplicitlyUnwrappedOptional (Person)
Как и 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
(all return 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
Вы можете использовать отображение, чтобы получить информацию об объекте.
Например, имя класса объекта:
var classname = reflect(now).summary
reflect(x).summary
- спасибо!
Мне повезло:
let className = NSStringFromClass(obj.dynamicType)
Xcode 8 Swift 3.0 тип использования (из:)
let className = "\(type(of: instance))"
let type : Type = MyClass.self //Determines Type from Class
let type : Type = type(of:self) //Determines Type from self
let string : String = "\(type)" //String
В Swift 3.0 вы можете использовать type(of:)
, поскольку ключевое слово 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()
а выход:
В верхнем ответе нет рабочего примера нового способа сделать это, используя 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'
В последнем XCode 6.3 с Swift 1.2 это единственный способ, которым я нашел:
if view.classForCoder.description() == "UISegment" {
...
}
<swift module name>.<actual class name>
, поэтому вы хотите разделить строку с помощью .
и получите последний жетон.
Я нашел это решение, которое, надеюсь, может работать для кого-то другого. Я создал метод класса для доступа к значению. Пожалуйста, имейте в виду, что это будет работать только для подкласса NSObject. Но, по крайней мере, это чистое и аккуратное решение.
class var className: String!{
let classString : String = NSStringFromClass(self.classForCoder())
return classString.componentsSeparatedByString(".").last;
}
Я пробовал некоторые другие ответы здесь, но milage, похоже, очень относится к тому, что является объектом underling.
Однако я нашел способ, которым вы можете получить имя класса 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 в консоли.
При использовании 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 в будущем.
EDIT: dump(object)
, похоже, делает трюк.
Многие из ответов здесь не работают с последним Swift (Xcode 7.1.1 на момент написания).
Текущий способ получения информации - создать Mirror
и опросить его. Для имени класса это просто:
let mirror = Mirror(reflecting: instanceToInspect)
let classname:String = mirror.description
Дополнительная информация об объекте также может быть получена из Mirror
. Подробнее см. http://swiftdoc.org/v2.1/type/Mirror/.
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)")
Основываясь на ответах и комментариях, полученных Класом и Кевином Баллардом выше, я бы пошел с:
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"
toString(self.dynamicType).componentsSeparatedByString(".").last!
Очевидно, я был в объектном контексте и мог ссылаться на self
.
Я нашел решение для саморазвивающихся классов (или у вас есть доступ).
Поместите следующее вычисленное свойство в определение класса объектов:
var className: String? {
return __FILE__.lastPathComponent.stringByDeletingPathExtension
}
Теперь вы можете просто вызвать имя класса на своем объекте так:
myObject.className
Обратите внимание, что это будет работать только в том случае, если ваше определение класса сделано в файле с именем точно, как класс, который вы хотите назвать.
Как обычно бывает в случае , вышеприведенный ответ должен делать это в большинстве случаев. Но в некоторых особых случаях вам может понадобиться найти другое решение.
Если вам нужно имя класса внутри самого класса (файла), вы можете просто использовать эту строку:
let className = __FILE__.lastPathComponent.stringByDeletingPathExtension
Возможно, этот метод помогает некоторым людям там.
В lldb с версии бета 5 вы можете увидеть класс объекта с помощью команды:
fr v -d r shipDate
который выводит что-то вроде:
(DBSalesOrderShipDate_DBSalesOrderShipDate_ *) shipDate = 0x7f859940
Развернутая команда означает что-то вроде:
Frame Variable
(напечатать переменную фрейма) -d run_target
(развернуть динамические типы)
Что-то полезное, чтобы знать, что использование "Frame Variable" для вывода значений переменных гарантирует, что код не будет выполнен.
Вот как вы получаете строку типа вашего объекта или типа, которая является согласованной и учитывает, к какому модулю принадлежит определение объекта или в который он вложен. Работает в 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
Swift версия 4:
print("\(type(of: self)) ,\(#function)")
// within a function of a class
Спасибо @Джошуа Дэнс
Кажется, что нет никакого общего способа печати типа имени произвольного типа значения. Как отмечали другие, для экземпляров классов вы можете печатать value.className
, но для примитивных значений появляется, что во время выполнения информация о типе отсутствует.
Например, похоже, что нет способа напечатать: 1.something()
и выйти Int
для любого значения something
. (Вы можете, как сказал другой ответ, использовать i.bridgeToObjectiveC().className
, чтобы дать вам подсказку, но __NSCFNumber
на самом деле не тип i
- то, что он будет преобразован, когда он пересекает границу Objective-C вызов функции.)
Я был бы рад оказаться ошибочным, но похоже, что проверка типов выполняется во время компиляции, и, как и С++ (с отключенным RTTI), большая часть информации о типе отсутствует во время выполнения.
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")
}
Xcode 7.3.1, Swift 2.2:
String(instanceToPrint.self).componentsSeparatedByString(".").last
Не совсем то, что вам нужно, но вы также можете проверить тип переменной по типам Swift:
let object: AnyObject = 1
if object is Int {
}
else if object is String {
}
Например.
// "TypeName"
func stringType (of some: Any) → String {let string = (some is Any.Type)? String(describing: some): String(describing: type(of: some)) return string } String (описывающий: some): String (описывающий: type (of: some)) возвращаемая строка}
// "ModuleName.TypeName"
func fullStringType (of some: Any) → String {let string = (some is Any.Type)? String(reflecting: some): String(reflecting: type(of: some)) return string } String (отражающий: некоторые): String (отражающий: тип (из: некоторые)) возвращаемая строка}
print(stringType(of: SomeClass())) // "SomeClass"
print(stringType(of: SomeClass.self)) // "SomeClass"
print(stringType(of: String())) // "String"
print(fullStringType(of: String())) // "Swift.String"
Чтобы получить тип объекта или класс объекта в Swift, вам необходимо использовать тип (of: yourObject)
тип (из: вашОбъект)
Другим важным аспектом, влияющим на имя класса, возвращаемого с String(describing: type(of: self))
, является Контроль доступа.
Рассмотрим следующий пример на основе Swift 3.1.1, Xcode 8.3.3 (июль 2017 г.)
func printClassNames() {
let className1 = SystemCall<String>().getClassName()
print(className1) // prints: "SystemCall<String>"
let className2 = DemoSystemCall().getClassName()
print(className2) // prints: "DemoSystemCall"
// private class example
let className3 = PrivateDemoSystemCall().getClassName()
print(className3) // prints: "(PrivateDemoSystemCall in _0FC31E1D2F85930208C245DE32035247)"
// fileprivate class example
let className4 = FileprivateDemoSystemCall().getClassName()
print(className4) // prints: "(FileprivateDemoSystemCall in _0FC31E1D2F85930208C245DE32035247)"
}
class SystemCall<T> {
func getClassName() -> String {
return String(describing: type(of: self))
}
}
class DemoSystemCall: SystemCall<String> { }
private class PrivateDemoSystemCall: SystemCall<String> { }
fileprivate class FileprivateDemoSystemCall: SystemCall<String> { }
Как вы можете видеть, все классы в этом примере имеют разные уровни контроля доступа, которые влияют на их представление String
. Если классы имеют уровни контроля доступа private
или fileprivate
, Swift, похоже, добавляет какой-то идентификатор, относящийся к классу "вложенности" рассматриваемого класса.
Результат для PrivateDemoSystemCall
и FileprivateDemoSystemCall
заключается в том, что тот же идентификатор добавляется, потому что они оба вложены в один и тот же родительский класс.
Я еще не нашел способ избавиться от этого, кроме функции хакерской замены или регулярного выражения.
Только мои 2 цента.
Пожалуйста, ознакомьтесь с приведенным ниже фрагментом кода и дайте мне знать, если вы ищете что-то вроде ниже или нет.
var now = NSDate()
var soon = now.addingTimeInterval(5.0)
var nowDataType = Mirror(reflecting: now)
print("Now is of type: \(nowDataType.subjectType)")
var soonDataType = Mirror(reflecting: soon)
print("Soon is of type: \(soonDataType.subjectType)")
Это также удобно при проверке, является ли объект типом класса:
if someObject is SomeClass {
//someObject is a type of SomeClass
}