J'ai tendance à ne mettre que les nécessités (propriétés stockées, initialiseurs) dans mes définitions de classe et à déplacer tout le reste dans les leurs extension
, un peu comme un extension
bloc logique avec lequel je grouperais // MARK:
également.
Pour une sous-classe UIView par exemple, je finirais avec une extension pour les trucs liés à la mise en page, une pour l'abonnement et la gestion des événements, etc. Dans ces extensions, je dois inévitablement remplacer certaines méthodes UIKit, par exemple layoutSubviews
. Je n'ai jamais remarqué de problèmes avec cette approche - jusqu'à aujourd'hui.
Prenez cette hiérarchie de classes par exemple:
public class C: NSObject {
public func method() { print("C") }
}
public class B: C {
}
extension B {
override public func method() { print("B") }
}
public class A: B {
}
extension A {
override public func method() { print("A") }
}
(A() as A).method()
(A() as B).method()
(A() as C).method()
La sortie est A B C
. Cela n'a pas de sens pour moi. J'ai lu que les extensions de protocole étaient distribuées statiquement, mais ce n'est pas un protocole. Il s'agit d'une classe régulière, et je m'attends à ce que les appels de méthode soient distribués dynamiquement au moment de l'exécution. Il est clair que l'appel en cours C
devrait au moins être distribué et produit de manière dynamique C
?
Si je supprime l'héritage NSObject
et crée C
une classe racine, le compilateur se plaint en disant declarations in extensions cannot override yet
, ce que j'ai déjà lu. Mais comment le fait d'avoir NSObject
comme classe racine change-t-il les choses?
Déplacer les deux surcharges dans leur déclaration de classe produit A A A
comme prévu, déplacer uniquement B
les produits A B B
, déplacer uniquement A
les produits C B C
, dont le dernier n'a absolument aucun sens pour moi: même pas celui typé statiquement pour A
produire le A
-output!
L'ajout du dynamic
mot - clé à la définition ou un remplacement semble me donner le comportement souhaité `` à partir de ce point dans la hiérarchie des classes vers le bas '' ...
Changeons notre exemple en quelque chose d'un peu moins construit, ce qui m'a fait poser cette question:
public class B: UIView {
}
extension B {
override public func layoutSubviews() { print("B") }
}
public class A: B {
}
extension A {
override public func layoutSubviews() { print("A") }
}
(A() as A).layoutSubviews()
(A() as B).layoutSubviews()
(A() as UIView).layoutSubviews()
Nous obtenons maintenant A B A
. Ici, je ne peux en aucun cas rendre le layoutSubviews de UIView dynamique.
Déplacer les deux remplacements dans leur déclaration de classe nous A A A
ramène, seuls les A ou seulement les B nous obtiennent encore A B A
. dynamic
résout à nouveau mes problèmes.
En théorie, je pourrais ajouter dynamic
à tout ce que override
je fais mais j'ai l'impression de faire quelque chose de mal ici.
Est-ce vraiment mal d'utiliser extension
s pour grouper du code comme je le fais?
la source
Réponses:
Les extensions ne peuvent / ne doivent pas être remplacées.
Il n'est pas possible de remplacer les fonctionnalités (comme les propriétés ou les méthodes) dans les extensions, comme indiqué dans le Guide Swift d'Apple.
Guide du développeur Swift
Le compilateur vous permet de remplacer l'extension pour la compatibilité avec Objective-C. Mais c'est en fait une violation de la directive sur la langue.
😊Cela m'a rappelé les " Trois lois de la robotique " d'Isaac Asimov 🤖
Les extensions ( sucre syntaxique ) définissent des méthodes indépendantes qui reçoivent leurs propres arguments. La fonction appelée
layoutSubviews
dépend du contexte que le compilateur connaît lorsque le code est compilé. UIView hérite de UIResponder qui hérite de NSObject donc le remplacement dans l'extension est autorisé mais ne devrait pas l'être .Il n'y a donc rien de mal à grouper, mais vous devez remplacer dans la classe et non dans l'extension.
Notes de directive
Vous ne pouvez
override
utiliser une méthode de superclasse queload()
initialize()
dans une extension de sous-classe si la méthode est compatible Objective-C.Par conséquent, nous pouvons voir pourquoi il vous permet de compiler en utilisant
layoutSubviews
.Toutes les applications Swift s'exécutent à l'intérieur du runtime Objective-C, sauf lors de l'utilisation de frameworks Swift uniquement qui permettent un runtime uniquement Swift.
Comme nous l'avons découvert, le runtime Objective-C appelle généralement deux méthodes principales de classe
load()
etinitialize()
automatiquement lors de l'initialisation des classes dans les processus de votre application.Concernant le
dynamic
modificateurÀ partir de la bibliothèque des développeurs Apple (archive.org)
Vous pouvez utiliser le
dynamic
modificateur pour exiger que l'accès aux membres soit distribué dynamiquement via le runtime Objective-C.Lorsque les API Swift sont importées par le moteur d'exécution Objective-C, il n'y a aucune garantie de répartition dynamique des propriétés, des méthodes, des indices ou des initialiseurs. Le compilateur Swift peut toujours dévirtualiser ou accéder aux membres en ligne pour optimiser les performances de votre code, en contournant le runtime Objective-C. 😳
Ainsi
dynamic
peut être appliqué à votrelayoutSubviews
->UIView Class
car il est représenté par Objective-C et l'accès à ce membre est toujours utilisé en utilisant le runtime Objective-C.C'est pourquoi le compilateur vous permet d'utiliser
override
etdynamic
.la source
L'un des objectifs de Swift est la répartition statique, ou plutôt la réduction de la répartition dynamique. Obj-C est cependant un langage très dynamique. La situation que vous voyez découle du lien entre les 2 langues et la manière dont elles travaillent ensemble. Il ne devrait pas vraiment compiler.
L'un des principaux points à propos des extensions est qu'elles sont destinées à étendre et non à remplacer / remplacer. Il ressort clairement du nom et de la documentation que telle est l'intention. En effet, si vous supprimez le lien vers Obj-C de votre code (supprimez en
NSObject
tant que superclasse), il ne se compilera pas.Ainsi, le compilateur essaie de décider de ce qu'il peut envoyer statiquement et de ce qu'il doit distribuer dynamiquement, et il passe par un vide à cause du lien Obj-C dans votre code. La raison
dynamic
`` fonctionne '' est que cela force la liaison Obj-C sur tout, donc tout est toujours dynamique.Donc, il n'est pas faux d'utiliser des extensions pour le regroupement, c'est génial, mais il est faux de remplacer les extensions. Tous les remplacements doivent être dans la classe principale elle-même et appeler des points d'extension.
la source
supportedInterfaceOrientations
dansUINavigationController
(dans le but d'afficher différentes vues dans différentes orientations), vous devez utiliser une classe personnalisée et non une extension? De nombreuses réponses suggèrent d'utiliser une extension pour remplacersupportedInterfaceOrientations
mais aimeraient des éclaircissements. Merci!Il existe un moyen de parvenir à une séparation nette de la signature de classe et de l'implémentation (dans les extensions) tout en conservant la possibilité d'avoir des remplacements dans les sous-classes. L'astuce consiste à utiliser des variables à la place des fonctions
Si vous vous assurez de définir chaque sous-classe dans un fichier source Swift séparé, vous pouvez utiliser des variables calculées pour les remplacements tout en gardant l'implémentation correspondante bien organisée en extensions. Cela contournera les «règles» de Swift et rendra l'API / signature de votre classe parfaitement organisée en un seul endroit:
...
...
L'extension de chaque classe peut utiliser les mêmes noms de méthode pour l'implémentation, car ils sont privés et non visibles l'un pour l'autre (tant qu'ils sont dans des fichiers séparés).
Comme vous pouvez le voir, l'héritage (en utilisant le nom de la variable) fonctionne correctement en utilisant super.variablename
la source
Cette réponse ne visait pas l'OP, à part le fait que je me sentais inspiré pour répondre par sa déclaration, «J'ai tendance à ne mettre que les nécessités (propriétés stockées, initialiseurs) dans mes définitions de classe et à déplacer tout le reste dans leur propre extension. .. ". Je suis principalement un programmeur C #, et en C #, on peut utiliser des classes partielles à cette fin. Par exemple, Visual Studio place les éléments liés à l'interface utilisateur dans un fichier source distinct à l'aide d'une classe partielle et laisse votre fichier source principal épuré afin que vous n'ayez pas cette distraction.
Si vous recherchez "classe partielle rapide", vous trouverez divers liens où les adhérents de Swift disent que Swift n'a pas besoin de classes partielles car vous pouvez utiliser des extensions. Il est intéressant de noter que si vous saisissez "extension rapide" dans le champ de recherche Google, sa première suggestion de recherche est "remplacement d'extension rapide", et pour le moment, cette question Stack Overflow est le premier appel. Je suppose que cela signifie que les problèmes avec (l'absence de) capacités de remplacement sont le sujet le plus recherché lié aux extensions Swift, et souligne le fait que les extensions Swift ne peuvent pas remplacer des classes partielles, du moins si vous utilisez des classes dérivées dans votre programmation.
Quoi qu'il en soit, pour couper court à une longue introduction, j'ai rencontré ce problème dans une situation où je voulais déplacer certaines méthodes standard / bagage hors des fichiers sources principaux des classes Swift que mon programme C #-to-Swift générait. Après avoir rencontré le problème de l'absence de remplacement autorisé pour ces méthodes après les avoir déplacées vers des extensions, j'ai fini par implémenter la solution de contournement simple suivante. Les principaux fichiers source Swift contiennent encore de minuscules méthodes de stub qui appellent les méthodes réelles dans les fichiers d'extension, et ces méthodes d'extension reçoivent des noms uniques pour éviter le problème de remplacement.
.
.
.
.
Comme je l'ai dit dans mon introduction, cela ne répond pas vraiment à la question du PO, mais j'espère que cette solution de contournement simple d'esprit pourrait être utile à ceux qui souhaitent déplacer des méthodes des fichiers source principaux vers des fichiers d'extension et exécuter le non - problème de dépassement.
la source
Utilisez POP (programmation orientée protocole) pour remplacer les fonctions des extensions.
la source