web-dev-qa-db-fra.com

Swift introspection de classe et génériques

J'essaie de créer dynamiquement un type basé sur une instance class à l'aide de génériques, mais je rencontre des difficultés avec l'introspection de classes.

Voici les questions:

  • Existe-t-il un équivalent de Swift à self.class de Obj-C?
  • Est-il possible d'instancier une classe à l'aide du résultat AnyClass de NSClassFromString?
  • Existe-t-il un moyen d'obtenir AnyClass ou sinon de taper des informations strictement à partir d'un paramètre générique T? (Similaire à la syntaxe typeof(T) de C #)
117
Erik

Eh bien, pour un, l'équivalent Swift de [NSString class] est .self (voir documents métatype , bien qu'ils soient assez minces).

En fait, NSString.class ne fonctionne même pas! Vous devez utiliser NSString.self.

let s = NSString.self
var str = s()
str = "asdf"

De même, avec une classe Swift, j'ai essayé ...

class MyClass {

}

let MyClassRef = MyClass.self

// ERROR :(
let my_obj = MyClassRef()

Hmm… l'erreur dit:

Echec de l'exécution du terrain de jeu: erreur:: 16: 1: erreur: la construction d'un objet de type classe 'X' avec une valeur de métatype requiert un initialiseur '@ requis'

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

Il m'a fallu un certain temps pour comprendre ce que cela signifiait… il s'est avéré qu'il souhaitait que la classe ait une @required init()

class X {
    func me() {
        println("asdf")
    }

    required init () {

    }
}

let Y = X.self

// prints "asdf"
Y().me()

Certains docs se réfèrent à ceci sous le nom de .Type, mais MyClass.Type me donne une erreur dans le terrain de jeu.

106
Jiaaro

Voici comment utiliser NSClassFromString. Vous devez connaître la super-classe de ce que vous allez finir avec. Voici une paire superclasse-sous-classe qui sait se décrire pour 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"}
}

Notez l'utilisation de la syntaxe spéciale @obj pour dicter le nom fourni Objective-C de ces classes; c'est crucial, car sinon nous ne connaissons pas la chaîne munged qui désigne chaque classe.

Maintenant, nous pouvons utiliser NSClassFromString pour créer la classe Zork ou la classe Zilk, car nous savons que nous pouvons le taper comme un objet NSObject et ne pas planter plus tard:

let aClass = NSClassFromString("Zork") as NSObject.Type
let anObject = aClass()
println(anObject) // "I am a Zork"

Et c'est réversible. println(NSStringFromClass(anObject.dynamicType)) fonctionne également.

47
matt

Si je lis bien la documentation, si vous traitez des instances et par exemple, voulez renvoyer une nouvelle instance du même type que l'objet qui vous a été fourni et le type peut être construit avec un init (), vous pouvez faire:

let typeOfObject = aGivenObject.dynamicType
var freshInstance = typeOfObject()

Je l'ai rapidement testé avec String:

let someType = "Fooo".dynamicType
let emptyString = someType()
let threeString = someType("Three")

qui a bien fonctionné.

13
monkeydom

Dans Swift

object.dynamicType

est obsolète.

Utilisez plutôt:

type(of:object)
13
J.beenie

implémentation rapide des types de comparaison

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

NOTE: Notez que cela fonctionne aussi avec les protocoles que l'objet peut ou non s'étendre

7
eonist

Voici un autre exemple montrant l'implémentation d'une hiérarchie de classes, similaire à la réponse acceptée, mis à jour pour la première version 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 ce résultat:

base
file
display
base
folder
display
1
possen

Enfin obtenu quelque chose à travailler. C'est un peu paresseux mais même la route NSClassFromString () ne m'a pas fonctionné ...

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

et bingo, "makeFoo" contient une instance Foo.

L'inconvénient est que vos classes doivent dériver de FactoryObject et elles DOIVENT avoir la méthode d'initialisation Obj-C + afin que votre classe soit automatiquement insérée dans la carte de classes par la fonction globale "mapClass".

1