In the area of access control, Swift provides five different access levels.
One, access control keywords
The following are listed from highest to lowest (entities: content modified by access level; Module: this project or other third party library) :
open
: allows access in the module that defines the entity, in other modules, allowing other modules to inherit, overwrite (open
Can only be used with classes or class members.public
: Allows access in the module that defines the entity or in other modules. Does not allow other modules to inherit or overwrite the entityinternal
: Allows access only in the module that defines the entity, not in other modulesfileprivate
: Allows access only in the source file that defines the entityprivate
: Allows access only in closed declarations that define entities
Most entities default to the internal level.
Two, the use of guidelines
An entity may not be defined by an entity with a lower level of access.
- Variable/constant type
p
Variable/constant - Parameter type/return value type
p
function - The parent class
p
A subclass - The parent agreement
p
The child agreement - The original type
p
typealias
- Original value type/associated value type
p
Enumerated type - Other types used to define type A
p
Type A - …
Example code 1 (error) :
fileprivate class Person {}internal var person: Person
Copy the code
This error occurs because the internal modifier person allows the module that defines the entity to access (Targets all Swift source files), while Fileprivate restricts the use of the person type to the current source file.
Example code 2 (correct) :
public class Person {}internal var person: Person
Copy the code
Anyway, in the above criteria,p
The access on the left must be greater than or equal to the access on the right.
Three, various types of access levels
3.1. Tuple types
The tuple type has the lowest level of access of all member types.
Sample code:
internal struct Dog {}fileprivate class Person {}fileprivate var data1: (Dog.Person)
private var data2: (Dog.Person)
Copy the code
Data1 and data2 are variables. (Dog, Person) is a tuple type. Person has a lower access level than Dog, so the tuple type access level is Fileprivate.
3.2. Generic types
The access level of a generic type is the lowest of the access level of the type and the access level of all generic type parameters.
Sample code:
internal class Car {}fileprivate class Dog {}public class Person<T1.T2> {}fileprivate var p = Person<Car.Dog> ()Copy the code
The access level of the generic type Person
is the lowest of Person’s and Car’s and Dog’s access levels, namely fileprivate.
3.3. Member/nested types
The access level of a type affects the default access level for members (properties, methods, initializers, subscripts) and nested types.
3.3.1. Typeprivate
orfileprivate
In general, if the type is private or Fileprivate, the member/nested type is also private or Fileprivate by default.
Sample code:
private class Person {
var age = 0
func run(a){}enum Seaon { case spring, summer }
}
Copy the code
The access level for the variable age, function run, and nested type Season is private.
3.3.2 rainfall distribution on 10-12. Typeinternal
orpublic
In general, if the type is internal or public, the member/nested type defaults to internal.
Example code 1:
public class Person {
var age = 0
func run(a){}enum Seaon { case spring, summer }
}
Copy the code
The access level for the variable age, function run, and nested type Season is internal.
Example code 2:
public class PublicClass { // public
public var p1 = 0 // internal
var p2 = 0 // internal
fileprivate func f1(a){}// fileprivate
private func f2(a){}// private
}
class InternalClass { // internal
var p = 9 // internal
fileprivate func f1(a){}// fileprivate
private func f2(a){}// private
}
fileprivate class FileprivateClass { // fileprivate
func f1(a){}// fileprivate
private func f2(a){}// private
}
private class PrivateClass { // private
func f(a){}// private
}
Copy the code
3.4. Rewriting of members
The access level of the member overridden by a subclass must be ≥ the access level of the subclass or ≥ the access level of the overridden member of the parent class.
3.4.1. Example code 1
A member of a parent class cannot be overridden by a child class defined outside the member scope.
Correct examples:
public class Person {
private var age: Int = 0
public class Student: Person {
override var age: Int {
set{}get { 10}}}}Copy the code
3.4.2. Example Code two
private class Person {}fileprivate class Student : Person {}Copy the code
Does the sample code above compile? It really depends.
-
If you put the sample code in the outermost layer (the main file is an example), it compiles fine.
-
If we put the sample code in the internal class:
Error: Parent class level needs to be greater than subclass level.
Class access restricted to the private Person class. The subclass has a higher access level than the parent class. If the code is placed in the outermost layer, whether private or fileprivate, the scope of the restriction is the entire source file, so no errors are reported.
3.4.3. Example Code 3
private struct Dog {
var age: Int = 0
func run(a){}}fileprivate struct Person {
var dog: Dog = Dog(a)mutating func walk(a) {
dog.run()
dog.age = 1}}Copy the code
And the example code is consistent, put in the class error, put in the source file root layer to compile normally.
But what if the Dog member variable and inner function in the code above had private access restrictions?
Error:
Analysis: Because Dog’s internal member variables and functions are controlled by private, they are only used within Dog and cannot be accessed externally.
Private, defined directly in global scope, is equivalent to Fileprivate.
3.4.4. Example Code 4
class Test {
private struct Dog {
var age: Int = 0
func run(a){}}private struct Person {
var dog: Dog = Dog(a)mutating func walk(a) {
dog.run()
dog.age = 1}}}Copy the code
Dog is private, but you can still use Dog and its internal attributes in Person. Attribute access rights in Dog are private by default, but when access rights are not explicitly declared, internal attributes have the same scope and type. So you can use Dog’s internal properties in Person.
3.5. The getter and setter
Getters and setters automatically receive the access level of their environment by default.
Setters can be given a separate access level lower than getters to restrict write permissions (getters cannot be given separate access).
Sample code:
fileprivate(set) public var num = 10
class Person {
private(set) var age = 0
fileprivate(set) public var weight: Int {
set{}get { 10}}internal(set) public subscript(index: Int) -> Int {
set{}get { index }
}
}
Copy the code
3.6. Initializer
- If a
public
classThe compile-generated default no-argument initializer that you want to invoke in another module must be explicitly providedpublic
The no-parameter initializer for. becausepublic
classThe default initializer forinternal
Level.
Example code 1:
public class Person {}var p = Person(a)Copy the code
Given that the above sample code is in a dynamic library, there is no way to call the no-argument initializer for Person in the current project, because everything inside a class that public decorates is internal by default.
If you want to use a no-parameter initializer for Person, you need to explicitly provide the no-parameter initializer and use a modifier above the public level.
Example code two (can be used by outsiders with a no-parameter initializer) :
public class Person {
public init(a){}}var p = Person(a)Copy the code
required
The initializerp
Its default access level.
Sample code:
public class Person {
fileprivate required init(a){}}Copy the code
Fileprivate must be replaced with a higher level of access.
- If the structure has
private/fileprivate
Store instance properties, so do their member initializersprivate/fileprivate
Otherwise, the default isinternal
.
Example code 1:
struct Point {
fileprivate var x = 0
var y = 0
}
var p = Point(x: 10, y: 20)
Copy the code
The access level for Point(x: 10, y: 20) is Fileprivate.
Example code 2:
struct Point {
private var x = 0
var y = 0
}
var p = Point(y: 20)
Copy the code
As long as one of the storage properties is private, all member initializers cannot be used (and the compiler no longer generates them automatically), only no-parameter initializers can be used.
3.7. Cases of enumerated types
An access level cannot be set separately for each case of an enum.
Each case automatically receives enum access level.
Public enums define cases that are also public (unlike structures/classes).
3.8. The agreement
Protocol definition requires (code) automatically receive protocol access level, cannot be set separately (similar to enumeration).
The access level of the protocol implementation must be greater than or equal to the access level of the type or protocol.
Example code 1:
internal protocol Runnable {
func run(a)
}
fileprivate class Person : Runnable {
public func run(a){}}Copy the code
Protocol implementation func run() {} access level ≥ (take the lowest access level of both type Person and protocol Runnable).
Example code 2:
public protocol Runnable {
func run(a)
}
public class Person : Runnable {
func run(a){}}Copy the code
Because classes that use public, their internal members and functions are internal, while protocols and classes are public, the lowest level of functions in a class should use public.
public class Person : Runnable {
public func run(a){}}Copy the code
3.9 extensions
-
If the extension’s access level is explicitly set, members added to the extension automatically receive the extension’s access level.
-
If the access level of the extension is not explicitly set, the default access level for members added by the extension is the same as for members defined directly in the type.
-
Access levels can be set separately for members added to the extension.
-
Extensions used for protocol compliance cannot be explicitly set to the access level of the extension.
- Extensions in the same file can be written as type declarations in multiple parts.
- Declare a private member in the original declaration that can be accessed in an extension of the same file
- Declare a private member in an extension that can be accessed in other extensions of the same file, in the original declaration
Sample code:
public class Person {
private func run0(a){}private func eat0(a) {
run1()
}
}
extension Person {
private func run1(a){}private func eat1(a) {
run0()
}
}
extension Person {
private func eat2(a) {
run1()
}
}
Copy the code
Whenever a class is extended in the same file, it simply splits the class into multiple declarations.
3.10. Assign the method to var/let
Methods can also be assigned to a let or var, like functions.
Sample code:
struct Person {
var age: Int
func run(_ v: Int) {
print("func run", age, v)
}
static func run(_ v: Int) {
print("static func run", v)
}
}
let fn1 = Person.run
fn1(10) // Static func run 10
let fn2: (Int) - > ()= Person.run
fn2(20) // Static func run 20
// The instance is passed to the variable
let fn3: (Person) - > ((Int) - > ())= Person.run
fn3(Person(age: 18(a))30) Func run 18 30
Copy the code
For more articles in this series, please follow our wechat official account [1024 Planet].