introspection - Swift clase introspección y genéricos
(7)
Implementación rápida de tipos de comparación
protocol Decoratable{}
class A:Decoratable{}
class B:Decoratable{}
let object:AnyObject = A()
object.dynamicType is A.Type//true
object.dynamicType is B.Type//false
object.dynamicType is Decoratable.Type//true
NOTA: Tenga en cuenta que también funciona con protocolos que el objeto puede o no extender
Intento crear dinámicamente un tipo basado en una instancia de clase usando genéricos, sin embargo, encuentro dificultades con la introspección de clases.
Aquí están las preguntas:
- ¿Hay un Swift equivalente a Obj-C''s
self.class
? - ¿Hay alguna manera de instanciar una clase usando el resultado
NSClassFromString
deNSClassFromString
? - ¿Hay alguna manera de obtener
AnyClass
o escribir información estrictamente a partir de un parámetro genéricoT
? (Similar a la sintaxistypeof(T)
C #)
Aquí hay otro ejemplo que muestra la implementación de la jerarquía de clases, similar a la respuesta aceptada, actualizada para la primera versión de Swift.
class NamedItem : NSObject {
func display() {
println("display")
}
required override init() {
super.init()
println("base")
}
}
class File : NamedItem {
required init() {
super.init()
println("folder")
}
}
class Folder : NamedItem {
required init() {
super.init()
println("file")
}
}
let y = Folder.self
y().display()
let z = File.self
z().display()
Imprime este resultado:
base
file
display
base
folder
display
Aquí se explica cómo usar NSClassFromString
. Tienes que saber la superclase de lo que vas a terminar. Aquí hay un par de subclase de superclase que saben cómo describirse a sí mismos para println
:
@objc(Zilk) class Zilk : NSObject {
override var description : String {return "I am a Zilk"}
}
@objc(Zork) class Zork : Zilk {
override var description : String {return "I am a Zork"}
}
Observe el uso de la sintaxis @obj
especial para dictar el nombre Objective-C munged de estas clases; eso es crucial, porque de lo contrario no conocemos la cadena maldita que designa a cada clase.
Ahora podemos usar NSClassFromString
para hacer la clase Zork o la clase Zilk, porque sabemos que podemos escribirlo como un NSObject y no bloquearlo más tarde:
let aClass = NSClassFromString("Zork") as NSObject.Type
let anObject = aClass()
println(anObject) // "I am a Zork"
Y es reversible; println(NSStringFromClass(anObject.dynamicType))
también funciona.
Bueno, para [NSString class]
, el equivalente Swift de [NSString class]
es .self
(ver documentos de Metatype , aunque son bastante delgados).
De hecho, NSString.class
ni siquiera funciona! Tienes que usar NSString.self
.
let s = NSString.self
var str = s()
str = "asdf"
Del mismo modo, con una clase rápida intenté ...
class MyClass {
}
let MyClassRef = MyClass.self
// ERROR :(
let my_obj = MyClassRef()
Hmm ... el error dice:
La ejecución de Playground falló: error:: 16: 1: error: la construcción de un objeto de clase tipo ''X'' con un valor de meta-tipo requiere un inicializador ''@required''
Y().me() ^ <REPL>:3:7: note: selected implicit initializer with type ''()'' class X { ^
Me tomó un tiempo descubrir lo que esto significa ... resulta que quiere que la clase tenga un @required init()
class X {
func me() {
println("asdf")
}
required init () {
}
}
let Y = X.self
// prints "asdf"
Y().me()
Algunos de los documentos se refieren a esto como .Type
, pero MyClass.Type
me da un error en el patio de recreo.
En veloz 3
object.dynamicType
es obsoleto.
En su lugar use:
type(of:object)
Finalmente conseguí algo para trabajar. Es un poco vago, pero incluso la ruta NSClassFromString () no funcionó para mí ...
import Foundation
var classMap = Dictionary<String, AnyObject>()
func mapClass(name: String, constructor: AnyObject) -> ()
{
classMap[name] = constructor;
}
class Factory
{
class func create(className: String) -> AnyObject?
{
var something : AnyObject?
var template : FactoryObject? = classMap[className] as? FactoryObject
if (template)
{
let somethingElse : FactoryObject = template!.dynamicType()
return somethingElse
}
return nil
}
}
import ObjectiveC
class FactoryObject : NSObject
{
@required init() {}
//...
}
class Foo : FactoryObject
{
class override func initialize()
{
mapClass("LocalData", LocalData())
}
init () { super.init() }
}
var makeFoo : AnyObject? = Factory.create("Foo")
y bingo, "makeFoo" contiene una instancia de Foo.
La desventaja es que tus clases deben derivar de FactoryObject y DEBEN tener el método de inicialización Obj-C + para que tu clase se inserte automágicamente en el mapa de clase mediante la función global "mapClass".
Si estoy leyendo la documentación correctamente, si maneja instancias y, por ejemplo, desea devolver una nueva instancia del mismo tipo que el objeto que se le ha otorgado y el tipo se puede construir con un init (), puede hacer lo siguiente:
let typeOfObject = aGivenObject.dynamicType
var freshInstance = typeOfObject()
Lo probé rápidamente con String:
let someType = "Fooo".dynamicType
let emptyString = someType()
let threeString = someType("Three")
que funcionó bien