2
votes

Se référer à 'soi' dans un contexte statique

Je suis ici pour comprendre pourquoi la solution que j'avais implémentée ne fonctionnera pas. En gros, j'ai une classe appelée MyClass et dans cette classe j'aimerais avoir un dictionnaire statique créé à partir d'un fichier plist. Comme ceci:

class MyClass {
    static func myDic() -> [String: String] {
        return NSDictionary(contentsOfFile: Bundle(for: self).path(forResource: "PlayerRolesWithColors", ofType: "plist")!) as! [String: String]
    }
}

Si je le fais, le compilateur se plaindra que:

Cannot convert value of type '(MyClass) -> () -> (MyClass)' to expected argument type 'AnyClass' (aka 'AnyObject.Type')

Mais si je change myDic code > var et créez une méthode statique retournant ce dic, tout va bien:

class MyClass {        
    static var myDic: [String: String] = NSDictionary(contentsOfFile: Bundle(for: self).path(forResource: "filename", ofType: "plist")!) as! [String: String]    
}

Deux questions ici:

  1. Que signifie ce sintax dans l'erreur du compilateur? '(MaClasse) -> () -> (MaClasse)'
  2. Quelle est la différence entre les deux cas? Pourquoi le premier ne fonctionne-t-il pas et le second est-il bien?

Merci.


1 commentaires

Bonnes questions, qui valent la peine d'être répondues :)


3 Réponses :


1
votes

Vous pouvez faire

class MyClass {
    static var myDic: [String: String] = NSDictionary(contentsOfFile: Bundle(identifier: "comThisBundle")!.path(forResource: "filename", ofType: "plist")!) as! [String: String]
}

Comme vous ne pouvez pas accéder à self dans une variable statique, ou utiliser l'identifiant du bundle

class MyClass {
    static var myDic: [String: String] = NSDictionary(contentsOfFile: Bundle(for:MyClass.self).path(forResource: "filename", ofType: "plist")!) as! [String: String]
}


0 commentaires

1
votes

Vous pouvez simplement utiliser le nom de la classe au lieu de self .

Vous ne devriez pas non plus utiliser NSDictionary puis effectuer un cast dans un Dictionary . Utilisez plutôt PropertyListDecoder .

class MyClass {
    static let myDic = try! PropertyListDecoder().decode([String:String].self, from: try! Data(contentsOf: Bundle(for: MyClass.self).url(forResource: "filename", withExtension: "plist")))
}


3 commentaires

Ce qui est malheureusement une très mauvaise solution (même si d'autres solutions sont actuellement impossibles). J'attends toujours github.com/apple / swift-evolution / blob / master / propositions /… à mettre en œuvre.


@Sulthan oui, pour les méthodes génériques, cette solution de contournement n'est pas appropriée et cette proposition serait formidable d'avoir implémenté


@ DávidPásztor Merci de m'avoir indiqué le PropertyListDecoder que je ne connaissais pas.



1
votes

Examinons un exemple plus simple (fonctionnel) gardant votre problème principal le même:

class ClassName {
    static func check() {
        print("static function check")
        print(type(of: self)) //ClassName.Type
        //same as
        print(type(of: ClassName.self)) //ClassName.Type
        
        //test
        print(type(of: self) == type(of: ClassName.self)) //true
    }
    
    func check() {
        print("normal function check")
        print(type(of: self)) //ClassName
        
        //test
        print(type(of: self) == type(of: ClassName.self)) //false
    }
}

ClassName.check()
ClassName().check()

Tout d'abord, notons que Bundle (for: AnyClass) prend un type d'objet.


1. Concernant les variables

Variables au niveau supérieur, accédez à self en tant que ClassName qui est un type d'instance, qu'il soit déclaré comme let code> / var / paresseux / calculé, statique ou non.

Donc:

static func getBundle() -> Bundle {
    return Bundle(for: self)
}

est identique à:

static var bundle = Bundle(for: ClassName.self)

Les deux ne sont pas valides et génèrent l'erreur suivante:

Impossible de convertir la valeur de type 'ClassName' en type d'argument attendu 'AnyClass' (alias 'AnyObject.Type')

Bien sûr, c'est parce que nous transmettons un type d'instance au lieu du type d'objet attendu.

Solution:

static var bundle = Bundle(for: ClassName())

2. Concernant les fonctions statiques

Comme pour une fonction statique, c'est un peu différent.

Le métatype sur lequel vous appelez la méthode statique est disponible dans la méthode en tant que self (il est simplement passé en tant que paramètre implicite).

Réf: https://stackoverflow.com/a/42260880/2857130 em>

Dans mon exemple, nous avons:

static var bundle = Bundle(for: self)

Lorsque vous appelez ClassName.getBundle () , ClassName. Le type est transmis implicitement à la fonction.
Désormais dans la fonction statique, self est de type ClassName.Type qui est un type Object et peut être appliqué directement dans Bundle (for :) , ou des fonctions similaires qui acceptent un type Object comme paramètre.

Ainsi, les fonctions statiques accèdent à self en tant que ClassName.Type qui est identique à ClassName.self , simplement pas apparent car il est passé implicitement.

Vous pouvez confirmer ce comportement de self dans une fonction static , et en outre, observez même comment self se comporte dans une fonction normale dans l'exemple suivant:

class ClassName {
    static var bundle = Bundle(for: ClassName.self)
    
    static func getBundle() -> Bundle {
        return Bundle(for: self)
    }
}

Nous montre également que les fonctions normales accèdent à self comme ClassName qui est un type d'instance, similaire aux variables.


Résumé:

  • Bundle (for :) prend un type d'objet
  • Variables au niveau supérieur, accédez à self en tant que ClassName qui est un type d'instance
  • Les fonctions normales accèdent à self en tant que ClassName qui est un type d'instance
  • Les fonctions statiques accèdent à self en tant que ClassName.Type qui est un type Object, car il est passé implicitement à la fonction


3 commentaires

Notez qu'il y a une grande différence entre les fonctions static et les fonctions class .


@Sulthan Bien sûr, il doit y en avoir (et nous n'avons pas parlé de fonctions class ) mais ... dans le contexte de sa question, cela n'aurait toujours pas d'importance si c'était un statique func ou class func comme type (of: self) dans les deux cas serait toujours ClassName.Type .


Pas dans une sous-classe mais vous avez raison, c'est probablement hors de portée de cette question.