Clase Swift introspección y genéricos


Estoy tratando de crear dinámicamente un tipo basado en instancia class usando genéricos, sin embargo estoy encontrando dificultades con la introspección de clase.

Aquí están las preguntas:

  • ¿Hay un Swift-equivalente a Obj-C self.class?
  • ¿Hay una manera de instanciar una clase usando el resultado AnyClass de NSClassFromString?
  • ¿Hay una manera de obtener AnyClass o de otra manera escribir información estrictamente a partir de un parámetro genérico T? (Similar a la sintaxis de C#typeof(T))
Author: Erik, 2014-06-05

7 answers

Bueno, para uno, el equivalente Swift de [NSString class] es .self (ver Metatipo docs, 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 swift lo intenté...

class MyClass {

}

let MyClassRef = MyClass.self

// ERROR :(
let my_obj = MyClassRef()

Hmm Hmm el error dice: {[14]]}

Playground execution failed: error:: 16: 1: error: construir un objeto de tipo de clase 'X' con un valor metatipo requiere un inicializador '@required'

 Y().me()
 ^
 <REPL>:3:7: note: selected implicit initializer with type '()'
 class X {
       ^

It me llevó un tiempo averiguar lo que esto significa turns 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.

 104
Author: Jiaaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-04 21:26:39

He aquí cómo usar NSClassFromString. Tienes que conocer la superclase con la que vas a terminar. Aquí hay un par superclase-subclase 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 especial @obj para dictar el nombre munged Objective-C de estas clases; eso es crucial, porque de lo contrario no conocemos la cadena munged que designa a cada clase.

Ahora podemos usar NSClassFromString para hacer la clase Zork o la clase Zilk, porque sabemos que podemos escribir es como un NSObject y no se bloquea 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.

 45
Author: matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-02 15:11:21

Si estoy leyendo bien la documentación, si trata con instancias y, por ejemplo, desea devolver una nueva instancia del mismo Tipo que el objeto que se le ha dado y el Tipo se puede construir con un init (), puede hacer:

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.

 13
Author: monkeydom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-10 00:47:43

In swift 3

object.dynamicType

Está en desuso.

En su lugar use:

type(of:object)
 12
Author: J.beenie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-27 02:33:47

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: Observe que también funciona con protocolos que el objeto puede o no extender

 7
Author: eonist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-16 07:34:32

Finalmente tengo algo que trabajar. Es un poco perezoso, 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.

El inconveniente es que sus clases deben derrive desde factoryObject y DEBEN tener el método Obj-C +initialize para que su clase se inserte automáticamente en el mapa de clases mediante la función global "mapClass".

 1
Author: Martin-Gilles Lavoie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-12 20:21:12

Aquí hay otro ejemplo que muestra la implementación de la jerarquía de clases, similar a accepted answer, 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
 1
Author: possen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-08 21:52:57