Puisque Swift prend en charge la surcharge de méthode et d'initialisation, vous pouvez mettre plusieurs à init
côté de l'autre et utiliser ce que vous jugez pratique:
class Person {
var name:String
init(name: String) {
self.name = name
}
init() {
self.name = "John"
}
}
Alors, pourquoi le convenience
mot-clé existerait-il même? Qu'est-ce qui améliore considérablement les éléments suivants?
class Person {
var name:String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "John")
}
}
swift
initialization
Desmond Hume
la source
la source
Réponses:
Les réponses existantes ne racontent que la moitié de l'
convenience
histoire. L'autre moitié de l'histoire, la moitié qu'aucune des réponses existantes ne couvre, répond à la question que Desmond a postée dans les commentaires:Je l'ai légèrement abordé dans cette réponse , dans laquelle je couvre en détail plusieurs règles d'initialisation de Swift, mais l'accent était principalement mis sur le
required
mot. Mais cette réponse concernait toujours quelque chose qui est pertinent pour cette question et cette réponse. Nous devons comprendre comment fonctionne l'héritage de l'initialisation Swift.Étant donné que Swift n'autorise pas les variables non initialisées, vous n'êtes pas assuré d'hériter de tous les initialiseurs (ou de certains) de la classe dont vous héritez. Si nous sous-classons et ajoutons des variables d'instance non initialisées à notre sous-classe, nous avons arrêté d'hériter des initialiseurs. Et jusqu'à ce que nous ajoutions nos propres initialiseurs, le compilateur nous hurlera dessus.
Pour être clair, une variable d'instance non initialisée est toute variable d'instance qui n'a pas de valeur par défaut (en gardant à l'esprit que les options et les options implicitement déballées prennent automatiquement la valeur par défaut de
nil
).Donc dans ce cas:
a
est une variable d'instance non initialisée. Cela ne se compilera que si nous donnonsa
une valeur par défaut:ou initialisez
a
dans une méthode d'initialisation:Maintenant, voyons ce qui se passe si nous sous
Foo
- classons , d'accord?Droite? Nous avons ajouté une variable, et nous avons ajouté un initialiseur pour définir une valeur
b
afin qu'il compile. En fonction de la langue que vous venez, vous pourriez attendre à ce queBar
a héritéFoo
de initialiseur de »,init(a: Int)
. Mais ce n'est pas le cas. Et comment le pourrait-il? CommentFoo
de »init(a: Int)
le savoir comment attribuer une valeur à lab
variableBar
ajoutée? Ce n'est pas le cas. Nous ne pouvons donc pas initialiser uneBar
instance avec un initialiseur qui ne peut pas initialiser toutes nos valeurs.Qu'est-ce que tout cela a à voir avec
convenience
?Eh bien, regardons les règles sur l'héritage d'initialiseur :
Remarquez la règle 2, qui mentionne les initialiseurs de commodité.
Ainsi, ce que fait le
convenience
mot - clé , c'est nous indiquer quels initialiseurs peuvent être hérités par des sous-classes qui ajoutent des variables d'instance sans valeurs par défaut.Prenons cet exemple de
Base
classe:Notez que nous avons trois
convenience
initialiseurs ici. Cela signifie que nous avons trois initialiseurs qui peuvent être hérités. Et nous avons un initialiseur désigné (un initialiseur désigné est simplement n'importe quel initialiseur qui n'est pas un initialiseur pratique).Nous pouvons instancier des instances de la classe de base de quatre manières différentes:
Alors, créons une sous-classe.
Nous héritons de
Base
. Nous avons ajouté notre propre variable d'instance et nous ne lui avons pas donné de valeur par défaut, nous devons donc ajouter nos propres initialiseurs. Nous avons ajouté un,init(a: Int, b: Int, c: Int)
mais il ne correspond pas à la signature de laBase
classe est désignée initialiseur:init(a: Int, b: Int)
. Cela signifie que nous n'héritons d' aucun initialiseur deBase
:Alors, que se passerait-il si nous héritions de
Base
, mais que nous allions de l'avant et implémentions un initialiseur qui correspondait à l'initialiseur désignéBase
?Maintenant, en plus des deux initialiseurs que nous avons implémentés directement dans cette classe, parce que nous avons implémenté l'initialiseur
Base
désigné d' une classe correspondant à un initialiseur, nous pouvons hériter de tousBase
lesconvenience
initialiseurs de classe :Le fait que l'initialiseur avec la signature correspondante soit marqué comme
convenience
ne fait aucune différence ici. Cela signifie seulement qu'ilInheritor
n'y a qu'un seul initialiseur désigné. Donc, si nous héritons deInheritor
, nous aurions juste à implémenter cet initialiseur désigné, puis nous hériterionsInheritor
de l'initialiseur de commodité, ce qui signifie que nous avons implémenté tousBase
les initialiseurs désignés et que nous pouvons hériter de sesconvenience
initialiseurs.la source
init(a: Int)
laisseraitb
non initialisé.Surtout la clarté. De votre deuxième exemple,
est requis ou désigné . Il doit initialiser toutes vos constantes et variables. Les initialiseurs de commodité sont facultatifs et peuvent généralement être utilisés pour faciliter l'initialisation. Par exemple, disons que votre classe Person a une variable de genre facultative:
où Gender est une énumération
vous pourriez avoir des initialiseurs pratiques comme celui-ci
Les initialiseurs de commodité doivent appeler les initialiseurs désignés ou requis. Si votre classe est une sous-classe, elle doit appeler
super.init()
dans son initialisation.la source
convenience
mot clé, mais Swift serait toujours en train de boguer à ce sujet. Ce n'est pas le genre de simplicité que j'attendais d'Apple =)Eh bien, la première chose qui me vient à l'esprit est qu'il est utilisé dans l'héritage de classe pour l'organisation et la lisibilité du code. Continuez avec votre
Person
classe, pensez à un scénario comme celui-ciAvec l'initialiseur pratique, je suis capable de créer un
Employee()
objet sans valeur, d'où le motconvenience
la source
convenience
mots clés supprimés, Swift n'obtiendrait-il pas suffisamment d'informations pour se comporter exactement de la même manière?convenience
mot-clé, vous ne pouvez pas initialiser l'Employee
objet sans aucun argument.Employee()
appelle l'convenience
initialiseur (hérité, dû à )init()
, qui appelleself.init(name: "Unknown")
.init(name: String)
, également un initialiseur pratique pourEmployee
, appelle l'initialiseur désigné.Outre les points que d'autres utilisateurs ont expliqués, voici mon peu de compréhension.
Je ressens fortement la connexion entre l'initialiseur de commodité et les extensions. Quant à moi, les initialiseurs de commodité sont les plus utiles lorsque je veux modifier (dans la plupart des cas, le rendre court ou facile) l'initialisation d'une classe existante.
Par exemple, une classe tierce que vous utilisez a
init
avec quatre paramètres mais dans votre application les deux derniers ont la même valeur. Pour éviter plus de frappe et rendre votre code propre, vous pouvez définir unconvenience init
avec seulement deux paramètres et à l'intérieur, appelerself.init
avec last to paramètres avec des valeurs par défaut.la source
convenience
devant mon initialiseur simplement parce que j'ai besoin d'appelerself.init
depuis celui-ci? Cela semble redondant et un peu gênant.Selon la documentation Swift 2.1 , les
convenience
initialiseurs doivent adhérer à certaines règles spécifiques:Un
convenience
initialiseur ne peut appeler les initiateurs que dans la même classe, pas dans les super classes (uniquement à travers, pas en haut)Un
convenience
initialiseur doit appeler un initialiseur désigné quelque part dans la chaîneUn
convenience
initialiseur ne peut pas changer de propriété ANY avant d'avoir appelé un autre initialiseur - alors qu'un initialiseur désigné doit initialiser les propriétés introduites par la classe actuelle avant d'appeler un autre initialiseur.En utilisant le
convenience
mot - clé, le compilateur Swift sait qu'il doit vérifier ces conditions - sinon il ne le pourrait pas.la source
convenience
mot clé.let
propriétés). Il ne peut pas initialiser les propriétés. Un initialiseur désigné a la responsabilité d'initialiser toutes les propriétés introduites avant d'appeler unsuper
initialiseur désigné.Une classe peut avoir plusieurs initialiseurs désignés. Un initialiseur pratique est un initialiseur secondaire qui doit appeler un initialiseur désigné de la même classe.
la source