the - swift(lenguaje de programación)
Obtener el nombre de clase del objeto como cadena en Swift (23)
Swift 3.1
Aquí está la extensión para obtener el className
como una variable (tanto para la instancia como para el objeto de clase).
extension NSObject {
var className: String {
return String(describing: type(of: self))
}
class var className: String {
return String(describing: self)
}
}
Cómo utilizar:
print(self.className)
print(ViewController.className)
Obtener el nombre de clase de un objeto como cadena usando
object_getClassName(myViewController)
devuelve algo como esto
_TtC5AppName22CalendarViewController
Estoy buscando la versión pura : "CalendarViewController". ¿Cómo consigo una cadena limpia del nombre de la clase?
Encontré algunos intentos de preguntas sobre esto, pero no una respuesta real. ¿No es posible?
A veces, las otras soluciones le darán un nombre no útil dependiendo del objeto que está tratando de mirar. En ese caso, puede obtener el nombre de la clase como una cadena utilizando lo siguiente.
String(cString: object_getClassName(Any!))
⌘ haga clic en la función en xcode para ver algunos métodos relacionados que son bastante útiles. o consulte aquí https://developer.apple.com/reference/objectivec/objective_c_functions
Cadena de una instancia :
String(describing: YourType.self)
Cadena de un tipo :
String(describing: self)
Ejemplo:
struct Foo {
// Instance Level
var typeName: String {
return String(describing: Foo.self)
}
// Instance Level - Alternative Way
var otherTypeName: String {
let thisType = type(of: self)
return String(describing: thisType)
}
// Type Level
static var typeName: String {
return String(describing: self)
}
}
Foo().typeName // = "Foo"
Foo().otherTypeName // = "Foo"
Foo.typeName // = "Foo"
Probado con class
, struct
y enum
.
En Swift 4.1 y ahora Swift 4.2 :
import Foundation
class SomeClass {
class InnerClass {
let foo: Int
init(foo: Int) {
self.foo = foo
}
}
let foo: Int
init(foo: Int) {
self.foo = foo
}
}
class AnotherClass : NSObject {
let foo: Int
init(foo: Int) {
self.foo = foo
super.init()
}
}
struct SomeStruct {
let bar: Int
init(bar: Int) {
self.bar = bar
}
}
let c = SomeClass(foo: 42)
let s = SomeStruct(bar: 1337)
let i = SomeClass.InnerClass(foo: 2018)
let a = AnotherClass(foo: 1<<8)
Si no tienes una instancia alrededor:
String(describing: SomeClass.self) // Result: SomeClass
String(describing: SomeStruct.self) // Result: SomeStruct
String(describing: SomeClass.InnerClass.self) // Result: InnerClass
String(describing: AnotherClass.self) // Result: AnotherClass
Si tienes una instancia en torno a:
String(describing: type(of: c)) // Result: SomeClass
String(describing: type(of: s)) // Result: SomeStruct
String(describing: type(of: i)) // Result: InnerClass
String(describing: type(of: a)) // Result: AnotherClass
En mi caso, String (describiendo: self) devolvió algo como:
<My_project.ExampleViewController: 0x10b2bb2b0>
Pero me gustaría tener algo como getSimpleName
en Android.
Así que he creado una pequeña extensión:
extension UIViewController {
func getSimpleClassName() -> String {
let describing = String(describing: self)
if let dotIndex = describing.index(of: "."), let commaIndex = describing.index(of: ":") {
let afterDotIndex = describing.index(after: dotIndex)
if(afterDotIndex < commaIndex) {
return String(describing[afterDotIndex ..< commaIndex])
}
}
return describing
}
}
Y ahora vuelve:
ExampleViewController
La extensión de NSObject en lugar de UIViewController también debería funcionar. La función anterior también es a prueba de fallos :)
He estado buscando esta respuesta de vez en cuando. Utilizo GKStateMachine y me gusta observar los cambios de estado y quería una forma fácil de ver solo el nombre de la clase. No estoy seguro de si es solo iOS 10 o Swift 2.3, pero en ese entorno, lo siguiente hace exactamente lo que quiero:
let state:GKState?
print("Class Name: /(String(state.classForCoder)")
// Output:
// Class Name: GKState
Intente reflect().summary
en la clase auto o instancia de tipo dinámico. Desenvuelva las opciones antes de obtener dynamicType; de lo contrario, el dynamicType es el contenedor opcional.
class SampleClass { class InnerClass{} }
let sampleClassName = reflect(SampleClass.self).summary;
let instance = SampleClass();
let instanceClassName = reflect(instance.dynamicType).summary;
let innerInstance = SampleClass.InnerClass();
let InnerInstanceClassName = reflect(innerInstance.dynamicType).summary.pathExtension;
let tupleArray = [(Int,[String:Int])]();
let tupleArrayTypeName = reflect(tupleArray.dynamicType).summary;
El resumen es una ruta de clase con los tipos genéricos descritos. Para obtener un nombre de clase simple del resumen, pruebe este método.
func simpleClassName( complexClassName:String ) -> String {
var result = complexClassName;
var range = result.rangeOfString( "<" );
if ( nil != range ) { result = result.substringToIndex( range!.startIndex ); }
range = result.rangeOfString( "." );
if ( nil != range ) { result = result.pathExtension; }
return result;
}
Las soluciones anteriores no funcionaron para mí. Producido en su mayoría los temas mencionados en varios comentarios
MyAppName.ClassName
o
MyFrameWorkName.ClassName
Estas soluciones funcionaron en XCode 9, Swift 3.0:
Lo nombré classNameCleaned
para que sea más fácil de acceder y no entre en conflicto con futuros cambios de className()
:
extension NSObject {
static var classNameCleaned : String {
let className = self.className()
if className.contains(".") {
let namesArray = className.components(separatedBy: ".")
return namesArray.last ?? className
} else {
return self.className()
}
}
}
Uso:
NSViewController.classNameCleaned
MyCustomClass.classNameCleaned
Lo uso en Swift 2.2
guard let currentController = UIApplication.topViewController() else { return }
currentController.classForCoder.description().componentsSeparatedByString(".").last!
Para obtener el nombre de la clase como String declara tu clase como sigue
@objc(YourClassName) class YourClassName{}
Y obtenga el nombre de la clase usando la siguiente sintaxis
NSStringFromClass(YourClassName)
Para obtener el nombre del tipo como una cadena en Swift 4 (no he revisado las versiones anteriores), solo use la interpolación de cadenas:
"/(type(of: myViewController))"
Puede usar .self
en un tipo en sí mismo, y la función type(of:_)
en una instancia:
// Both constants will have "UIViewController" as their value
let stringFromType = "/(UIViewController.self)"
let stringFromInstance = "/(type(of: UIViewController()))"
Puede extender NSObjectProtocol
en Swift 4 de la siguiente manera:
import Foundation
extension NSObjectProtocol {
var className: String {
return String(describing: Self.self)
}
}
Esto hará que la variable calculada className
esté disponible para TODAS las clases. Al usar esto dentro de un print()
en CalendarViewController
se imprimirá "CalendarViewController"
en la consola.
Puede usar la función de la biblioteca estándar de Swift llamada _stdlib_getDemangledTypeName
esta manera:
let name = _stdlib_getDemangledTypeName(myViewController)
Puedes intentarlo de esta manera:
self.classForCoder.description()
Si no te gusta el nombre destrozado, puedes dictar tu propio nombre:
@objc(CalendarViewController) class CalendarViewController : UIViewController {
// ...
}
Sin embargo, sería mejor a largo plazo aprender a analizar el nombre mutilado. El formato es estándar y significativo y no cambiará.
Si tiene el tipo Foo
, el siguiente código le dará "Foo"
en Swift 3 y Swift 4:
let className = String(describing: Foo.self) // Gives you "Foo"
El problema con la mayoría de las respuestas aquí es que le dan "Foo.Type"
como la cadena resultante cuando no tiene ninguna instancia del tipo, cuando lo que realmente quiere es simplemente "Foo"
. Lo siguiente le da "Foo.Type"
, como se menciona en un montón de las otras respuestas.
let className = String(describing: type(of: Foo.self)) // Gives you "Foo.Type"
El type(of:)
parte es innecesario si solo quieres "Foo"
.
Sugiero tal enfoque ( muy Swifty ):
// Swift 3
func typeName(_ some: Any) -> String {
return (some is Any.Type) ? "/(some)" : "/(type(of: some))"
}
// Swift 2
func typeName(some: Any) -> String {
return (some is Any.Type) ? "/(some)" : "/(some.dynamicType)"
}
No utiliza ni introspección ni desmantelamiento manual (¡ no hay magia! ).
Aquí hay una demostración:
// Swift 3
import class Foundation.NSObject
func typeName(_ some: Any) -> String {
return (some is Any.Type) ? "/(some)" : "/(type(of: some))"
}
class GenericClass<T> {
var x: T? = nil
}
protocol Proto1 {
func f(x: Int) -> Int
}
@objc(ObjCClass1)
class Class1: NSObject, Proto1 {
func f(x: Int) -> Int {
return x
}
}
struct Struct1 {
var x: Int
}
enum Enum1 {
case X
}
print(typeName(GenericClass<Int>.self)) // GenericClass<Int>
print(typeName(GenericClass<Int>())) // GenericClass<Int>
print(typeName(Proto1.self)) // Proto1
print(typeName(Class1.self)) // Class1
print(typeName(Class1())) // Class1
print(typeName(Class1().f)) // (Int) -> Int
print(typeName(Struct1.self)) // Struct1
print(typeName(Struct1(x: 1))) // Struct1
print(typeName(Enum1.self)) // Enum1
print(typeName(Enum1.X)) // Enum1
Swift 3.0 (macOS 10.10 y versiones posteriores), puede obtenerlo de className
self.className.components(separatedBy: ".").last!
Swift 3.0
String(describing: MyViewController.self)
También se pueden usar espejos:
let vc = UIViewController()
String(Mirror(reflecting: vc).subjectType)
NB: este método también se puede utilizar para Estructuras y Enums. Hay un displayStyle que da una indicación de qué tipo de estructura:
Mirror(reflecting: vc).displayStyle
El regreso es una enumeración para que puedas:
Mirror(reflecting: vc).displayStyle == .Class
ACTUALIZADO A SWIFT 3
Podemos obtener descripciones bonitas de los nombres de tipo utilizando la variable de instancia a través del inicializador de String
y crear nuevos objetos de una clase determinada
Como, por ejemplo, print(String(describing: type(of: object)))
. Donde el object
puede ser una variable de instancia como una matriz, un diccionario, un Int
, un NSDate
, etc.
Debido a que NSObject
es la clase raíz de la mayoría de las jerarquías de clases de Objective-C, puede intentar hacer una extensión para que NSObject
obtenga el nombre de clase de cada subclase de NSObject
. Me gusta esto:
extension NSObject {
var theClassName: String {
return NSStringFromClass(type(of: self))
}
}
O podría hacer una función estática cuyo parámetro sea de tipo Any
(el protocolo al que todos los tipos cumplen implícitamente) y devuelve el nombre de la clase como Cadena. Me gusta esto:
class Utility{
class func classNameAsString(obj: Any) -> String {
//prints more readable results for dictionaries, arrays, Int, etc
return String(describing: type(of: obj))
}
}
Ahora puedes hacer algo como esto:
class ClassOne : UIViewController{ // some code here }
class ClassTwo : ClassOne{ // some code here }
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Get the class name as String
let dictionary: [String: CGFloat] = [:]
let array: [Int] = []
let int = 9
let numFloat: CGFloat = 3.0
let numDouble: Double = 1.0
let classOne = ClassOne()
let classTwo: ClassTwo? = ClassTwo()
let now = NSDate()
let lbl = UILabel()
print("diccionary: [String: CGFloat] = [:] -> /(Utility.classNameAsString(diccionary))")
print("array: [Int] = [] -> /(Utility.classNameAsString(array))")
print("int = 9 -> /(Utility.classNameAsString(int))")
print("numFloat: CGFloat = 3.0 -> /(Utility.classNameAsString(numFloat))")
print("numDouble: Double = 1.0 -> /(Utility.classNameAsString(numDouble))")
print("classOne = ClassOne() -> /((ClassTwo?).self)") //we use the Extension
if classTwo != nil {
print("classTwo: ClassTwo? = ClassTwo() -> /(Utility.classNameAsString(classTwo!))") //now we can use a Forced-Value Expression and unwrap the value
}
print("now = Date() -> /(Utility.classNameAsString(now))")
print("lbl = UILabel() -> /(String(describing: type(of: lbl)))") // we use the String initializer directly
}
}
Además, una vez que podamos obtener el nombre de la clase como String, podemos crear una instancia de los nuevos objetos de esa clase :
// Instanciate a class from a String
print("/nInstaciate a class from a String")
let aClassName = classOne.theClassName
let aClassType = NSClassFromString(aClassName) as! NSObject.Type
let instance = aClassType.init() // we create a new object
print(String(cString: class_getName(type(of: instance))))
print(instance.self is ClassOne)
Tal vez esto ayude a alguien por ahí !.
Swift 3.0: puedes crear una extensión como esta ... Devuelve el nombre de la clase sin el nombre del proyecto
extension NSObject {
var className: String {
return NSStringFromClass(self as! AnyClass).components(separatedBy: ".").last ?? ""
}
public class var className: String {
return NSStringFromClass(self).components(separatedBy: ".").last ?? ""
}
}