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.

25
demandé sur Community 2016-10-25 23:55:35

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éfaut private.
  • Si votre classe est marquée internal (qui est par défaut), public, ou open, toutes les variables, inits, et les fonctions par défaut internal.
  • 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 comme open, 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 ou fileprivate, il semble que les membres de la classe par défaut fileprivate, sauf si explicitement annoté private.
20
répondu Matthew Seaman 2016-10-25 22:55:57