Contrôle d'accès Swift

Le contrôle d'accès peut restreindre le niveau d'accès du code dans d'autres fichiers sources ou modules à votre code.

Vous pouvez définir explicitement des niveaux d'accès pour des types individuels (classes, structures, énumérations), et également définir des niveaux d'accès pour les propriétés, fonctions, initialiseurs, types de base, indices, etc. de ces types.

Les protocoles peuvent également être limités à une certaine portée, y compris les constantes globales, les variables et les fonctions dans le protocole.

Le contrôle d'accès est basé sur des modules et des fichiers sources.

Un module désigne un framework ou une application construit et publié comme une unité indépendante. En Swift, un module peut utiliser le mot-clé import pour importer un autre module.

Un fichier source est un fichier de code source unique, qui appartient généralement à un module. Un fichier source peut contenir les définitions de plusieurs classes et fonctions.

Swift propose quatre niveaux d’accès différents pour les entités dans le code :public、internal、fileprivate、private。

Niveau d’accès Définition
public Peut accéder à toute entité dans les fichiers source de son propre module, et d’autres peuvent également accéder à toutes les entités des fichiers source en important le module.
internal Peut accéder à toute entité dans les fichiers source au sein de son propre module, mais d’autres ne peuvent pas accéder aux entités des fichiers source de ce module.
fileprivate Privé au fichier, ne peut être utilisé que dans le fichier source actuel.
private On ne peut y accéder qu’au sein de la classe ; une fois en dehors de la portée de la classe ou de la structure, il est inaccessible.

public est le niveau d’accès le plus élevé, et private est le niveau d’accès le plus bas.

Syntaxe

Utilisez les modificateurs public, internal, fileprivate et private pour déclarer le niveau d’accès d’une entité :

Exemple

public class SomePublicClass {} internal class SomeInternalClass {} fileprivate class SomeFilePrivateClass {} private class SomePrivateClass {} public var somePublicVariable = 0 internal let someInternalConstant = 0 fileprivate func someFilePrivateFunction() {} private func somePrivateFunction() {}

Sauf indication contraire, les entités utilisent le niveau d’accès par défaut internal.

Le niveau d’accès non spécifié vaut internal par défaut

class SomeInternalClass {} // Le niveau d’accès est internal let someInternalConstant = 0 // Le niveau d’accès est internal

Contrôle d’accès des types de fonctions

Le niveau d’accès d’une fonction est déterminé par les niveaux d’accès de ses types de paramètres et de son type de retour.

L'exemple suivant définit une fonction globale nommée someFunction sans déclarer explicitement son niveau d'accès.

func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函数实现
}

L'une des classes dans la fonction, SomeInternalClass, a un niveau d'accès internal, et une autre, SomePrivateClass, a un niveau d'accès private. Par conséquent, selon le principe du niveau d'accès des tuples, le niveau d'accès du tuple est private (le niveau d'accès du tuple est le même que le niveau d'accès le plus bas parmi les types du tuple).

Comme le type de retour de la fonction a un niveau d'accès private, vous devez utiliser le modificateur private pour déclarer explicitement la fonction :

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函数实现
}

Déclarer la fonction comme public ou internal, ou utiliser le niveau d'accès par défaut internal, est incorrect, car si vous le faites, vous ne pouvez pas accéder à la valeur de retour de niveau private.


Contrôle d'accès des types d'énumération

Le niveau d'accès des membres d'une énumération est hérité de l'énumération ; vous ne pouvez pas déclarer individuellement des niveaux d'accès différents pour les membres de l'énumération.

Exemple

Par exemple, dans l'exemple suivant, l'énumération Student est explicitement déclarée comme public, donc ses membres Name et Mark ont aussi le niveau d'accès public :

Exemple

public enum Student { case Name(String) case Mark(Int,Int,Int) } var studDetails = Student.Name("Swift") var studMarks = Student.Mark(98,97,95) switch studMarks { case .Name(let studName): print("Nom de l'étudiant :\(studName).") case .Mark(let Mark1, let Mark2, let Mark3): print("Score de l'étudiant :\(Mark1),\(Mark2),\(Mark3)") }

La sortie de l'exécution du programme ci-dessus est :

学生成绩: 98,97,95

Contrôle d'accès des sous-classes

Le niveau d'accès d'une sous-classe ne doit pas être supérieur à celui de sa superclasse. Par exemple, si la superclasse a un niveau d'accès interne, la sous-classe ne peut pas être déclarée publique.

Exemple

public class SuperClass { fileprivate func show() { print("Superclass") } } // Le niveau d'accès ne peut pas être supérieur à celui de la superclasse : public > internal internal class SubClass: SuperClass { override internal func show() { print("Subclass") } } let sup = SuperClass() sup.show() let sub = SubClass() sub.show()

La sortie de l'exécution du programme ci-dessus est :

Superclass

Contrôle d'accès pour les constantes, variables, propriétés et indices

Les constantes, les variables et les propriétés ne peuvent pas avoir un niveau d'accès supérieur à celui de leurs types.

Par exemple, si vous définissez une propriété avec un niveau d'accès public mais que son type est private, le compilateur ne l'autorisera pas.

De même, un indice ne peut pas avoir un niveau d'accès supérieur à celui de son type d'index ou de son type de retour.

Si le type défini d'une constante, d'une variable, d'une propriété ou d'un index d'indice est private, ils doivent alors déclarer explicitement leur niveau d'accès comme private :

private var privateInstance = SomePrivateClass()

Contrôle d'accès des getters et des setters

Les niveaux d'accès des getters et des setters pour les constantes, les variables, les propriétés et les index d'indices sont hérités du niveau d'accès du membre auquel ils appartiennent.

Le niveau d'accès d'un setter peut être inférieur à celui du getter correspondant, ce qui vous permet de contrôler les autorisations de lecture et d'écriture des variables, des propriétés ou des index d'indices.

Exemple

class Samplepgm { fileprivate var counter: Int = 0{ willSet(newTotal){ print("Compteur :\(newTotal)") } didSet{ if counter > oldValue { print("Nouveau montant d'incrémentation\(counter - oldValue)") } } } } let NewCounter = Samplepgm() NewCounter.counter = 100 NewCounter.counter = 800

Le niveau d'accès de counter est fileprivate, il est donc accessible dans le fichier.

La sortie de l'exécution du programme ci-dessus est :

计数器: 100
新增加数量 100
计数器: 800
新增加数量 700

Contrôle d'accès des initialiseurs et des initialiseurs par défaut

Initialisation

Nous pouvons déclarer des niveaux d'accès pour les initialiseurs personnalisés, mais ils ne doivent pas être supérieurs au niveau d'accès de la classe à laquelle ils appartiennent. Toutefois, les initialiseurs requis font exception ; leur niveau d'accès doit être identique au niveau d'accès de la classe à laquelle ils appartiennent.

Tout comme les paramètres de fonction ou de méthode, les niveaux d'accès des paramètres d'initialiseur ne peuvent pas être inférieurs au niveau d'accès de l'initialiseur.

Méthodes d'initialiseur par défaut

Swift fournit un initialiseur par défaut sans paramètre pour les structures et les classes afin de fournir des opérations d'affectation pour toutes leurs propriétés, mais il ne donne pas de valeurs spécifiques.

Le niveau d'accès d'un initialiseur par défaut est le même que le niveau d'accès de son type conteneur.

Exemple

Utilisez le mot-clé required avant la méthode init() de chaque sous-classe pour déclarer les permissions d'accès.

Exemple

class classA { required init() { var a = 10 print(a) } } class classB: classA { required init() { var b = 30 print(b) } } let res = classA() let show = classB()

La sortie de l'exécution du programme ci-dessus est :

10
30
10

Contrôle d'accès aux protocoles

Si vous souhaitez déclarer explicitement un niveau d'accès pour un protocole, notez que vous devez vous assurer que le protocole n'est utilisé que dans la portée du niveau d'accès que vous avez déclarée.

Si vous définissez un protocole avec un niveau d'accès public, les fonctions requises qui implémentent le protocole auront également un niveau d'accès public. Cela diffère des autres types. Par exemple, pour les autres types avec un niveau d'accès public, leurs membres ont un niveau d'accès interne.

Exemple

public protocol TcpProtocol { init(no1: Int) } public class MainClass { var no1: Int // stockage local init(no1: Int) { self.no1 = no1 // initialisation } } class SubClass: MainClass, TcpProtocol { var no2: Int init(no1: Int, no2 : Int) { self.no2 = no2 super.init(no1:no1) } // Ne nécessite qu'un seul paramètre pour une méthode pratique required override convenience init(no1: Int) { self.init(no1:no1, no2:0) } } let res = MainClass(no1: 20) let show = SubClass(no1: 30, no2: 50) print("res est : \(res.no1)") print("res est : \(show.no1)") print("res est : \(show.no2)")

La sortie de l'exécution du programme ci-dessus est :

res is: 20
res is: 30
res is: 50

Contrôle d’accès des extensions

Vous pouvez étendre des classes, des structures et des énumérations lorsque les conditions le permettent. Les membres d’extension doivent avoir le même niveau d’accès que les membres de la classe d’origine. Par exemple, si vous étendez un type public, vos membres nouvellement ajoutés doivent avoir le même niveau d’accès interne par défaut que les membres d’origine.

Vous pouvez également déclarer explicitement le niveau d’accès de l’extension (par exemple en utilisant private extension) pour déclarer un nouveau niveau d’accès par défaut pour tous les membres de l’extension. Ce nouveau niveau d’accès par défaut peut encore être remplacé par les niveaux d’accès déclarés par des membres individuels.


Contrôle d’accès générique

Le niveau d’accès d’un type générique ou d’une fonction générique est le niveau d’accès le plus faible parmi le type générique, la fonction elle-même et les paramètres de type génériques.

Exemple

public struct TOS<T> { var items = [T]() private mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } } var tos = TOS<String>() tos.push("Swift") print(tos.items) tos.push("Générique") print(tos.items) tos.push("Paramètre de type") print(tos.items) tos.push("Nom du paramètre de type") print(tos.items) let deletetos = tos.pop()

La sortie de l'exécution du programme ci-dessus est :

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "类型参数"]
["Swift", "泛型", "类型参数", "类型参数名"]

Alias de type

Tout alias de type que vous définissez sera traité comme un type différent aux fins du contrôle d'accès. Le niveau d'accès d'un alias de type ne peut pas être supérieur au niveau d'accès du type d'origine.

Par exemple, un alias de type de niveau private peut être défini pour un type public, internal ou private, mais un alias de type de niveau public ne peut être défini que pour un type public, et ne peut pas être défini pour des types internal ou private.

Remarque : cette règle s'applique également lors du nommage d'alias pour des types associés afin de satisfaire à la conformité au protocole.

Exemple

public protocol Container { typealias ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } struct Stack<T>: Container { // implémentation originale de Stack<T> var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } // conformité au protocole Container mutating func append(item: T) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> T { return items[i] } } func allItemsMatch< C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> (someContainer: C1, anotherContainer: C2) -> Bool { // vérifier que les deux conteneurs contiennent le même nombre d'éléments if someContainer.count != anotherContainer.count { return false } // vérifier chaque paire d'éléments pour voir s'ils sont équivalents for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // tous les éléments correspondent, donc return true return true } var tos = Stack<String>() tos.push("Swift") print(tos.items) tos.push("Generic") print(tos.items) tos.push("Instruction Where") print(tos.items) var eos = ["Swift", "Generic", "Instruction Where"] print(eos)

La sortie de l'exécution du programme ci-dessus est :

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "Where 语句"]
["Swift", "泛型", "Where 语句"]
Autres extensions