Swift access control

Access control can limit the level of access to your code by code in other source files or modules.

You can explicitly set access levels for individual types (classes, structures, enumerations), or you can set access levels for properties, functions, initialization methods, basic types, subsumed indexes, and so on.

Protocols can also be used to a limited extent, including global constants, variables, and functions in the protocol.

Access control is based on modules and source files.

Modules refer to Framework or Application built and published in separate units. One module in Swift can use the sport keyword to introduce another module.

A source file is a single source file that typically belongs to a module that can contain the definition of multiple classes and functions.

Swift provides three different levels of access to entities in your code: public, internal, private.

The access level Defined
Public You can access any entity in the source file in your own module, and others can access all entities in the source file by introducing the module.
Internal : You can access any entity in the source file in your own module, but others cannot access entities in the source file in that module.
Private An entity, called a private entity, that can only be used in the current source file.

Public is the highest level of access and private is the lowest level of access.

Grammar

The access level of the entity is declared by the modifier public, internal, private:

public class SomePublicClass {}
internal class SomeInternalClass {}
private class SomePrivateClass {}

public var somePublicVariable = 0
internal let someInternalConstant = 0
private func somePrivateFunction() {}

Entities use the default access level internal unless there are special instructions.


Function type access

The access level of a function needs to be derived based on the parameter type of the function and the access level of the return type.

The following example defines a global function called homeFunction and does not explicitly state its access level.

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

One of the classes in the function, SomeInternalClass, has an access level of internal, and the other, SomePrivateClass, has an access level of private. So according to the principle of the metadata access level, the metadata access level is private.

Because the access level of the function return type is private, you must use the private modifier to explicitly declare the function:
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函数实现
}

It would be wrong to state the function as public or internal, or to use the default access level internal.


Enumere type access

The access level of a member in an enumerity is inherited from that enumerity, and you cannot individually state a different access level for a member in an enumerity.

Instance

For example, in the following example, enumeraling Student is explicitly stated as a public level, and its members Name, Mark's access level is also public:

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("学生名: \(studName).")
case .Mark(let Mark1, let Mark2, let Mark3):
    print("学生成绩: \(Mark1),\(Mark2),\(Mark3)")
}

The output of the above program execution is:

学生成绩: 98,97,95

Sub-class access

The access level of the child class must not be higher than the access level of the parent class. F or example, the access level of the parent class is internal, and the access level of the child class cannot be stated as public.

public class SuperClass {
    private func show() {
        print("超类")
    }
}

// 访问级别不能低于超类 internal > public
internal class SubClass: SuperClass  {
    override internal func show() {
        print("子类")
    }
}

let sup = SuperClass()
sup.show()

let sub = SubClass()
sub.show()

The output of the above program execution is:

超类
子类

Constants, variables, properties, underseed access

Constants, variables, and properties cannot have a higher level of access than their types.

For example, you define a public-level property, but its type is private-level, which the compiler does not allow.

Similarly, the underseed cannot have a higher level of access than the index type or return type.

If the defined types of constants, variables, properties, and underlying indexes are private levels, they must explicitly state that the access level is private:

private var privateInstance = SomePrivateClass()

Getter and Setter access

The access levels for constants, variables, properties, underseed indexes for Getters and Setters are inherited from the access levels of the members to which they belong.

Setter's access level can be lower than the corresponding Getter's, which allows it to control read and write permissions for variables, properties, or substations.

class Samplepgm {
    private var counter: Int = 0{
        willSet(newTotal){
            print("计数器: \(newTotal)")
        }
        didSet{
            if counter > oldValue {
                print("新增加数量 \(counter - oldValue)")
            }
        }
    }
}

let NewCounter = Samplepgm()
NewCounter.counter = 100
NewCounter.counter = 800

The output of the above program execution is:

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

Constructor and default constructor access

Initialization

We can state the access level to the custom initialization method, but not higher than the access level of the class to which it belongs. T he necessary constructor exception, however, must have the same level of access as the class it belongs to.

Like function or method parameters, the access level of the initialized method parameter cannot be lower than the access level of the initialized method.

The default initialization method

Swift provides a default non-participating initialization method for structures and classes to assign all of their properties, but does not give specific values.

The access level of the default initialization method is the same as the access level of the type to which it belongs.

Instance

Claim access using the required keyword before the init() method of each sub-class.

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()

The output of the above program execution is:

10
30
10

Protocol access

If you want to explicitly state the access level for an agreement, it is important to note that you want to ensure that the protocol is used only in the access level scope that you affirm.

If you define a protocol at the public access level, the necessary functions to implement the protocol are also the public access level. This is different from other types, such as other types of public access levels, where members have internal access levels.

public protocol TcpProtocol {
    init(no1: Int)
}

public class MainClass {
    var no1: Int // local storage
    init(no1: Int) {
        self.no1 = no1 // initialization
    }
}

class SubClass: MainClass, TcpProtocol {
    var no2: Int
    init(no1: Int, no2 : Int) {
        self.no2 = no2
        super.init(no1:no1)
    }
    
    // Requires only one parameter for convenient method
    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 is: \(res.no1)")
print("res is: \(show.no1)")
print("res is: \(show.no2)")

The output of the above program execution is:

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

Extend access

You can extend classes, structures, and enumerations as conditions permit. E xtended members should have access levels consistent with the original class members. F or example, if you extend a common type, your new member should have the same default internal access level as the original member.

Alternatively, you can explicitly state the extended access level, such as using the private extension, to give all members of the extension a new default access level. This new default access level can still be overwritten by the access level stated by individual members.


Generic access

The access level of a generic type or generic function is the lowest of the generic type, the function itself, and the generic type parameter.

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("泛型")
print(tos.items)

tos.push("类型参数")
print(tos.items)

tos.push("类型参数名")
print(tos.items)
let deletetos = tos.pop()

The output of the above program execution is:

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

The type alias

Any type alias you define is treated as a different type for access control. The access level of a type alias must not be higher than that of the original type.

For example, a private-level type alias can be set to a public, internal, private type, but a public-level type alias can only be set to a public-level type, not an internal or private-level type.

Note: This rule also applies if the relevant type is aliased to satisfy protocol consistency.

public protocol Container {
    typealias ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

struct Stack<T>: Container {
    // original Stack<T> implementation
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T {
        return items.removeLast()
    }
    
    // conformance to the Container protocol
    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 {
        // check that both containers contain the same number of items
        if someContainer.count != anotherContainer.count {
            return false
        }
        
        // check each pair of items to see if they are equivalent
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        
        // all items match, so return true
        return true
}

var tos = Stack<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
print(tos.items)

tos.push("Where 语句")
print(tos.items)

var eos = ["Swift", "泛型", "Where 语句"]
print(eos)

The output of the above program execution is:

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