Swift - 访问控制


限制对代码块、模块和抽象的访问是通过访问控制来完成的。类、结构体和枚举可以通过访问控制机制根据其属性、方法、初始值设定项和下标进行访问。协议中的常量、变量和函数通过访问控制受到限制并允许作为全局和局部访问。应用于属性、类型和函数的访问控制可以称为“实体”。

访问控制模型基于模块和源文件。

模块被定义为代码分发的单个单元,可以使用关键字“import”导入。源文件被定义为模块中的单个源代码文件,用于访问多种类型和函数。

Swift 4 语言提供了三种不同的访问级别。它们是公共、内部和私人访问。

序列号 访问级别和定义
1

民众

允许使用来自其定义模块的任何源文件(来自导入定义模块的另一个模块的源文件)来处理实体。

2

内部的

允许实体在其定义模块的任何源文件中使用,但不能在该模块外部的任何源文件中使用。

3

私人的

将实体的使用限制为其自己定义的源文件。私有访问起到隐藏特定代码功能的实现细节的作用。

句法

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

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

函数类型的访问控制

某些函数可能在函数内部声明了参数,但没有任何返回值。以下程序将 a 和 b 声明为 sum() 函数的参数。在函数本身内部,参数 a 和 b 的值通过调用函数调用 sum() 来传递,并打印其值,从而消除返回值。要使函数的返回类型为私有,请使用 private 修饰符声明函数的总体访问级别。

private func sum(a: Int, b: Int) {
   let a = a + b
   let b = a - b
   print(a, b)
}

sum(a: 20, b: 10)
sum(a: 40, b: 10)
sum(a: 24, b: 6)

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

30 20
50 40
30 24

枚举类型的访问控制

public enum Student {
   case Name(String)
   case Mark(Int,Int,Int)
}
var studDetails = Student.Name("Swift 4")
var studMarks = Student.Mark(98,97,95)

switch studMarks {
   case .Name(let studName):
      print("Student name is: \(studName).")
   case .Mark(let Mark1, let Mark2, let Mark3):
      print("Student Marks are: \(Mark1),\(Mark2),\(Mark3).")
   
}

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

Student Marks are: 98,97,95

Swift 4 语言中的枚举会针对枚举的个别情况自动接收相同的访问级别。例如,考虑访问在三个科目中保护的学生姓名和分数,枚举名称被声明为学生,枚举类中存在的成员是属于字符串数据类型的名称,分数表示为数据类型 Integer 的 mark1、mark2 和 mark3。访问学生姓名或他们的分数。现在,如果执行该 case 块,则 switch case 将打印学生姓名,否则它将打印学生保护的分数。如果两个条件都失败,则将执行默认块。

子类的访问控制

Swift 4 允许用户对可以在当前访问上下文中访问的任何类进行子类化。子类不能具有比其超类更高的访问级别。用户被限制编写内部超类的公共子类。

public class cricket {
   internal func printIt() {
      print("Welcome to Swift 4 Super Class")
   }
}

internal class tennis: cricket {
   override internal func printIt() {
      print("Welcome to Swift 4 Sub Class")
   }
}

let cricinstance = cricket()
cricinstance.printIt()

let tennisinstance = tennis()
tennisinstance.printIt()

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

Welcome to Swift Super Class
Welcome to Swift Sub Class

常量、变量、属性和下标的访问控制

Swift 4 常量、变量或属性不能定义为其类型以外的公共类型。用私有类型编写公共属性是无效的。同样,下标不能比其索引或返回类型更公开。

当常量、变量、属性或下标使用私有类型时,常量、变量、属性或下标也必须标记为私有 -

private var privateInstance = SomePrivateClass()

Getter 和 Setter

常量、变量、属性和下标的 getter 和 setter 自动接收与其所属常量、变量、属性或下标相同的访问级别。

class Samplepgm {
   var counter: Int = 0{
      willSet(newTotal) {
         print("Total Counter is: \(newTotal)")
      }
      didSet {
         if counter > oldValue {
            print("Newly Added Counter \(counter - oldValue)")
         }
      }
   }
}

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

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

Total Counter is: 100
Newly Added Counter 100
Total Counter is: 800
Newly Added Counter 700

初始化程序和默认初始化程序的访问控制

可以为自定义初始值设定项分配小于或等于它们初始化的类型的访问级别。所需的初始值设定项必须与其所属的类具有相同的访问级别。初始化程序的参数类型不能比初始化程序自己的访问级别更私有。

要声明初始化的每个子类,需要在 init() 函数之前定义“required”关键字。

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

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

10
30
10

默认初始化程序与其初始化的类型具有相同的访问级别,除非该类型被定义为公共。当默认初始化被定义为公共时,它被认为是内部的。当用户需要在另一个模块中使用无参数初始值设定项来初始化公共类型时,请显式提供公共无参数初始值设定项作为类型定义的一部分。

协议的访问控制

当我们定义一个新协议来继承现有协议的功能时,必须将两者声明为相同的访问级别以继承彼此的属性。Swift 4 访问控制不允许用户定义继承自“内部”协议的“公共”协议。

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 print = subClass(no1: 30, no2: 50)

print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

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

分机的访问控制

当用户使用扩展来添加协议一致性时,Swift 4 不允许用户为扩展提供显式访问级别修饰符。扩展中每个协议要求实现的默认访问级别都提供有其自己的协议访问级别。

泛型的访问控制

泛型允许用户指定访问其类型参数的类型约束的最低访问级别。

public struct TOS<T> {
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
   mutating func pop() -> T {
      return items.removeLast()
   }
}

var tos = TOS<String>()
tos.push(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Type Parameters")
print(tos.items)

tos.push(item: "Naming Type Parameters")
print(tos.items)
let deletetos = tos.pop()

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

[Swift 4]
[Swift 4, Generics]
[Swift 4, Generics, Type Parameters]
[Swift 4, Generics, Type Parameters, Naming Type Parameters]

类型别名的访问控制

用户可以定义类型别名来处理不同的访问控制类型。用户可以定义相同的访问级别或不同的访问级别。当类型别名为“私有”时,其关联成员可以声明为“私有,公共类型的内部”。当类型别名为公共时,成员不能别名为“内部”或“私有”名称

出于访问控制的目的,您定义的任何类型别名都将被视为不同的类型。类型别名的访问级别可以小于或等于它所别名的类型的访问级别。例如,私有类型别名可以为私有、内部或公共类型添加别名,但公共类型别名不能为内部或私有类型添加别名。

public protocol Container {
   associatedtype 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: 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(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Where Clause")
print(tos.items)

var eos = ["Swift 4", "Generics", "Where Clause"]
print(eos)

当我们使用 Playground 运行上述程序时,我们得到以下结果 -

[Swift 4]
[Swift 4, Generics]
[Swift 4, Generics, Where Clause]
[Swift 4, Generics, Where Clause]

Swift 编码和解码

Swift 4 引入了新的Codable协议,让您可以序列化和反序列化自定义数据类型,而无需编写任何特殊代码 - 并且不必担心丢失值类型。

struct Language: Codable {
   var name: String
   var version: Int
}
let swift = Language(name: "Swift", version: 4)
let java = Language(name: "java", version: 8)
let R = Language(name: "R", version: 3

请注意,语言符合可编码协议。现在,我们将使用一行简单的代码将其转换为 Json 数据表示形式。

let encoder = JSONEncoder()
if let encoded = try? encoder.encode(java) {
   //Perform some operations on this value.
}

Swift 会自动对数据类型中的所有值进行编码。

您可以使用 Decoder 函数对数据进行解码,例如

let decoder = JSONDecoder()
if let decoded = try? decoder.decode(Language.self, from: encoded) {
   //Perform some operations on this value.
}

JSONEncoder 及其对应的属性列表 PropertyListEncoder 都有很多用于自定义其工作方式的选项。