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.
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 .
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
Vous pouvez remplacer les déclarations des extensions tant que vous @objc
z 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
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.
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...
}
}
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.