Pourquoi une classe/structure publique dans Swift nécessite-t-elle un initialiseur public explicite?
Considérez la classe suivante (également applicable à une structure) dans un module:
public class Foo {
public func bar() {
// method body
}
}
Notez qu'il n'a pas d'initialiseur explicite; cet exemple n'a pas besoin d'initialisation spéciale. Cette classe serait exposée à d'autres modules car elle est marquée public
. Cependant, lorsque le code en dehors du module tente de l'initialiser, le compilateur se plaint:
let foo = Foo() // 'Foo' initializer is inaccessible due to 'internal' protection level
Afin de satisfaire le compilateur, je dois définir un initialiseur vide explicite marqué public
:
public class Foo {
public init() {
// This initializer intentionally left empty
}
public func bar() {
// do something useful
}
}
Pourquoi, si la classe est explicitement public
, dois-je définir explicitement un initialiseur public? Ne devrait-il pas implicitement avoir un initialiseur public?
Il y a une question connexe ici, concernant les tests unitaires, mais je trouve que cela n'est pas vraiment au cœur de la philosophie de conception de ce que je trouve être un problème surprenant.
1 réponses
Marquer une classe publique n'implique pas nécessairement que le développeur souhaite que la classe soit initialisée publiquement. Par exemple, j'écris souvent des classes de base qui existent uniquement pour que je puisse les sous-classer. Je donne à ces superclasses internal
des initialiseurs pour que leurs sous-classes puissent y accéder, mais ceux du monde extérieur ne devraient pas les utiliser directement. Par exemple, Operation
dans Foundation
n'a pas d'initialiseurs accessibles, mais la classe est publique. Il est simplement destiné à être sous-classé. C'est considéré comme une classe abstraite dans Objective-C.
Comme Swift ne contient pas de support explicite pour les classes abstraites, le fait de rendre une classe publique mais sans initialiseurs publics sert essentiellement de classe abstraite (sauf que chaque fonction doit toujours avoir une définition par défaut, soit dans la classe elle-même ou une extension de protocole).
Dans cet esprit, voici quelques règles Swift:
- Si votre classe est marquée
private
, toutes les variables, inits, et les fonctions valeur par défautprivate
. - Si votre classe est marquée
internal
(qui est par défaut),public
, ouopen
, toutes les variables, inits, et les fonctions par défautinternal
. - la superclasse d'une sous-classe doit être au moins aussi accessible.
- les classes et les membres de classe déclarés
public
dans Objective-C sont importés dans Swift commeopen
, en raison de l'absence d'une telle distinction dans Objective-C.
Ce second est celui que vous rencontrez. L'initialisation par défaut récupère la valeur par défaut internal
parce que la dernière chose que Swift veut faire est d'exposer votre init en tant QU'API publique, à moins qu'il ne lui soit explicitement demandé de le faire.
Note: Dans mes tests (au moins dans le terrain de jeu), il semble qu'avec l'introduction de fileprivate
:
- Si une classe est déclarée
private
oufileprivate
, il semble que les membres de la classe par défautfileprivate
, sauf si explicitement annotéprivate
.