web-dev-qa-db-fra.com

Les déclarations d'extensions ne peuvent pas encore être remplacées dans Swift 4

J'ai récemment migré mon code vers Swift 4 . Il y a un problème auquel je suis confronté avec extensions , i.e

Les déclarations d'extensions ne peuvent pas encore être remplacées

J'ai déjà lu plusieurs messages reclassant ce problème. Mais aucun d’entre eux ne divertit le scénario décrit ci-dessous:

class BaseCell: UITableViewCell
{
    //Some code here...
}

extension BaseCell
{
    func isValid() -> String?
    {
        //Some code here...
    }
}

class SampleCell: BaseCell
{
    //Some code here...

    override func isValid() -> String? //ERROR..!!!
    {
        //Some code here...
    }
}

Selon Apple,

Les extensions peuvent ajouter de nouvelles fonctionnalités à un type, mais elles ne peuvent pas remplacer les fonctionnalités existantes.

Mais dans le scénario ci-dessus, je ne remplace pas la méthode isValid() en extension. Il est remplacé dans la définition de la classe SampleCell. Pourtant, c'est donner l'erreur.

11
PGDev

Mais dans le scénario ci-dessus, je ne remplace pas la méthode isValid() dans une extension.

isValid est déclaré dans un extension .

L'erreur dit à peu près que si une fonction est déclarée de cette façon, elle ne peut être remplacée .

L'instruction est valable pour à partir d'une extension et dans une extension .

9
Tamás Sengel

Dans Swift 3, vous pouviez remplacer la fonction d’extension si l’extension appartenait à une classe dérivée de Objective-C ( http://blog.flaviocaetano.com/post/this-is-how-to- override-extension-methods/ ), mais je suppose que ce n'est pas possible maintenant dans Swift 4. Vous pouvez bien sûr faire quelque chose comme ceci: 

protocol Validity {
    func isValid() -> String?
}

class BaseCell: UITableViewCell, Validity {

}

extension Validity
{
    func isValid() -> String? {
        return "false"
    }
}

class SampleCell: BaseCell {

    func isValid() -> String? {
        return "true"
    }
}


let base = BaseCell()
base.isValid() // prints false

let sample = SampleCell()
sample.isValid() // prints true
4
Puneet Sharma

Vous pouvez remplacer les déclarations des extensions tant que vous @objcz le protocole. Dans Swift 4.2:

class BaseClass {}
class SubclassOfBaseClass: BaseClass {}

@objc protocol IsValidable {
    func isValid() -> Bool
}

extension BaseClass: IsValidable {
    func isValid() -> Bool { return false }
}

extension SubclassOfBaseClass {
    override func isValid() -> Bool { return !super.isValid() }
}

BaseClass().isValid()           // -> false
SubclassOfBaseClass().isValid() // -> true
4
Adam Preble

Je pense que cela s’explique de soi . les déclarations DES EXTENSIONS ne peuvent pas encore être remplacées

Vous essayez de override la fonction func isValid() -> String? qui a été déclarée dans une extension de BaseCell, pas la classe BaseCell elle-même.

Il est clairement indiqué que vous ne pouvez pas remplacer un élément déclaré dans une extension.

J'espère que c'est utile.

2
Tarun Tyagi

Moi aussi, j'avais un énorme héritage de code Swift 3 qui utilisait ce vieux truc pour réaliser ce que je voulais. Par conséquent, lorsque je suis passé à Swift 4 et que j'ai commencé à avoir ces erreurs, j'étais un peu en détresse. Ne crains rien, il existe une solution.

Cette erreur est liée à la manière dont Swift 4 compile les classes et à la nouvelle façon de traiter les classes et les fonctions Objective-C. Sous Swift 3, si une classe est dérivée de NSObject, toutes les variables et fonctions de cette classe utiliseraient les conventions de dénomination dynamique et de recherche de Objective-C. Cette approche a empêché Swift d'optimiser le code et d'améliorer ses performances et sa taille. 

Pour surmonter ces pénalités, dans Swift 4, seules les variables et les fonctions explicitement marquées avec @objc obtiennent le traitement Objective-C, tout le reste utilisant les conventions Swift standard: d'où l'erreur. 

Forts de cette connaissance, la solution à votre problème consiste à baliser les fonctions de l'extension que vous souhaitez remplacer par @objc, puis dans les classes enfant, substituez la fonction, mais n'oubliez pas d'inclure la balise @objc pour que votre code soit appelé à runtime. 

WARNINGIl y a peu de choses qui se passent ici: si vous oubliez d'inclure le @objc dans la override, le compilateur ne se plaindra pas, mais votre code n'a pas la recherche dynamique, donc il n'est jamais appelé à l'exécution.

Donc, votre code devrait ressembler un peu à ça:

class BaseCell: UITableViewCell {
    //Some code here...
}

extension BaseCell {
    @objc func isValid() -> String? {
        //Some code here...
    }
}

class SampleCell: BaseCell {
    //Some code here...

    @objc override func isValid() -> String? {
        //Some code here...
    }
}
1
pbc

Il n'est pas valide dans Swift, mais pas dans Objective-C. Ainsi, si la signature de votre méthode le permet (pas de constructions interdites objc), vous pouvez le déclarer @objc func myMethod() et le remplacer librement dans Swift.

0
nobre