Swift基础入门知识学习(26)-访问控制(存取控制)-讲给你懂

Swift基础入门知识学习(26)-访问控制(存取控制)-讲给你懂,第1张

TED演讲的8个秘诀:学习18分钟高效表达-重点笔记

Swift基础入门知识学习(25)-泛型-讲给你懂

目录 存取层级存取层级基本原则预设存取层级 自定义类型元组类型函数类型枚举类型嵌套(巢状)类型 子类别常量, 变量, 属性及下标Getter 与 Setter 构造器预设构造器结构体的成员逐一构造器协定协定继承协定一致性 扩展经由扩展来遵循协定 泛型类型别名


理解难度
★★★★☆
实用程度
★★★☆☆

Swift 提供访问控制(access control)的特性,让你可以为程式码或模组设置存取权限,决定哪些部分可以开放给外部程式码使用。

Swift 中,可以为枚举、结构体或类别设置存取权限,这些类型内部的属性、方法、下标或构造器也可以设置存取权限。而协定中的全域变量、常量或函数也可以设置存取权限。

如果你只是单纯的在开发一个独立的应用程式,而不是开发一个模组,其实可以不用显式设置存取权限,Swift 已为大部分的情况提供预设存取权限。

Swift 的访问控制主要是基于模组(module)和源文件(source file)这两个概念。模组指的是一个独立的程式码组件,在 Swift 中,可以在一个模组中使用import关键字来引用另一个模组。以 Xcode 来说,每个 target (也就是框架framework或应用程式application)都是一个独立的模组。另外如果将一些通用功能或常用程式打包成独立的框架,这也是一种模组。源文件表示一个 Swift 的源程式文件( Swift source code file ),通常会属于在一个模组内。 存取层级

Swift 提供了 5 种不同的存取层级,分别如下:

Open:公开存取的层级,同模组中的任何程式及外部引用这个模组的程式都可以存取、继承类别或是重写类别成员(即属性或方法)。Public:公开存取的层级,以存取来说,规则与 Open 层级一样。不同的是,只有在其定义的同模组内的程式可以继承类别或是重写类别成员,外部引用这个模组的程式则无法。Internal:内部存取的层级,同模组的源文件中的程式可以使用,但外部引用的程式不能使用。当定义为只供应用程式或模组内部使用时,可以将其设为internal。File-private:私有存取的层级,只能在原始定义的源文件中使用。使用fileprivate可以用来隐藏特定功能的实作。Private:私有存取的层级,只能在原始定义的程式区域中(像是一个类别内)使用。使用private可以用来隐藏特定功能的实作。

使用方式为在变量、函数或类别的前面加上这 5 种不同的关键字来声明它们的存取层级,如下:


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

存取层级基本原则

Swift 存取层级的基本原则:不可以在一个实例中定义存取层级限制更严格的实例。如以下的例子:

一个public的变量,不能将它的类型定义为internal、fileprivate或private。因为当变量可以被公开存取,但定义它的类型不行,这样会出现错误。一个函数的参数、返回类型存取层级限制不能比函数本身的更严格。因为当函数存取层级设为public,可以被公开存取时,但参数或返回类型存取层级设为internal、fileprivate或private,无法被公开存取,这样会出现错误。 预设存取层级

如果没有显性声明存取层级时,internal就是预设的存取层级。


class SomeInternalClass {}              // 访问级别为 internal
let someInternalConstant = 0   
         // 访问级别为 internal
自定义类型

你也可以为自定义的类型定义存取层级,当然你必须确认这个类型的作用范围与存取层级的限制相符。

类别的存取层级会影响其内部成员的存取层级,像是定义一个private的类别,则其内部成员的存取层级都会是private。而定义一个public或internal的类别,其内部成员都会是internal。

一个public类别,其内成员预设存取层级为internal而不是public,如果要为某个成员设为public,必须显式定义。这样在你定义一个模组的公开介面时,可以明确的选择哪些介面是公开的,避免不小心将内部使用的介面公开。

以下是几个类别与其内部成员存取层级的例子:


// 显式指定为 public 类别
public class SomePublicClass {
    // 显式指定为 public 成员
    public var somePublicProperty = 0

    // 隐式推断为 internal 成员
    var someInternalProperty = 0

    // 显式指定为 fileprivate 成员
    fileprivate func someFilePrivateMethod() {}

    // 显式指定为 private 成员
    private func somePrivateMethod() {}
}

// 隐式推断为 internal 类别
class SomeInternalClass {
    // 隐式推断为 internal 成员
    var someInternalProperty = 0

    // 显式指定为 fileprivate 成员
    fileprivate func someFilePrivateMethod() {}

    // 显式指定为 private 成员
    private func somePrivateMethod() {}
}

// 显式指定为 private 类别
private class SomePrivateClass {
    // 隐式推断为 private 成员
    var somePrivateProperty = 0

    // 隐式推断为 private 成员
    func somePrivateMethod() {}
}

元组类型

元组不能明确指定存取层级,而是在使用时被自动推断的。元组的存取层级会根据其内成员存取层级最严格的一个为准,例子如下:

private var description = "Sunny day !"
internal var number = 300
public var name = "Joe Black"

// 这时这个元组的存取层级会根据最严格的 private 为准
let someTuple = (description, number, name)
函数类型

函数的存取层级是根据存取层级最严格的参数或返回值类型来决定。经由此规则得到的存取层级如果与预设存取层级不一样,则必须明确的指定函数的存取层级,如下:


// 定义一个用来当做 函数返回值类型 的类别
private class SomeClass {}

// 定义一个函数
// 返回值为 SomeClass 存取层级为 private
// 则这个函数的存取层级也为 private
// 这时与预设的 internal 不一样 所以必须明确指定函数为 private
// 如果将函数前面的 private 拿掉 会报错误
private func someFunction() -> SomeClass {
    return SomeClass()
}

下面的例子定义了一个名为someFunction全局函数,并且没有明确地申明其访问级别。

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

函数中其中一个类 SomeInternalClass 的访问级别是 internal,另一个 SomePrivateClass 的访问级别是 private。所以根据元组访问级别的原则,该元组的访问级别是 private(元组的访问级别与元组中访问级别最低的类型一致)。
因为该函数返回类型的访问级别是 private,所以你必须使用 private 修饰符,明确的声明该函数:


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

将该函数申明为 public 或 internal,或者使用默认的访问级别 internal 都是错误的,因为如果这样你就无法访问 private 级别的返回值。

枚举类型

枚举成员的存取层级与枚举相同,无法为枚举成员单独指定不同的存取层级,如下:


// 定义枚举的存取层级为 public
public enum CompassPoint {
    // 则枚举成员都为 public
    case north
    case south
    case east
    case west
}

而枚举的原始值(raw value)与相关值(associated value)的存取层级限制不能比枚举的存取层级严格。例如,你不能在一个internal的枚举中定义private的原始值。

实例
比如下面的例子,枚举 Student 被明确的申明为 public 级别,那么它的成员 Name,Mark 的访问级别同样也是 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)")
    
}

嵌套(巢状)类型

以巢状类型来说,定义在private类型中的巢状类型,会自动指定为private。而在public或internal类型中,巢状类型则自动指定为internal,这时如果要指定巢状类型为public,则必须明确指定为public。

子类别

子类的存取层级限制不能比父类更为宽松,例如父类为internal时,子类就不能是public。

在符合当前存取层级限制的条件下:

子类可以重写父类任意的成员(方法、属性、构造器或下标等),用来提供限制较宽松的存取层级。子类成员可以存取限制更严格的父类成员(因为子类与父类定义在同一个源文件中)。

以下是一个例子:


// 定义一个 public 的类别 A
public class A {
    // 定义一个 private 的方法
    private func someMethod() {}
}

// 继承自 A 的类别 B 其存取层级为 internal
// 符合 子类的存取层级限制不能比父类更为宽松
internal class B: A {
    // 可以重写父类的方法 更新为较宽松的存取层级
    // (当然必须符合自身的存取层级)
    override internal func someMethod() {
        // 可以呼叫 存取层级限制更严格的父类成员
        super.someMethod()
    }
}

常量, 变量, 属性及下标

常量、变量及属性不能拥有比它们的类型限制更为宽松的存取层级。例如,不能定义一个public的属性,但它的类型却是private。而下标也不能拥有比它们的索引值或返回值类型限制更为宽松的存取层级。以下是一个例子:


// 定义一个 private 的类别
private class SomeClass {}

// 这时变量的类型为 private 则变量必须显式的定义为 private
// 将 private 拿掉会报错误 因为会变成预设的 internal 则与规则不符
private var someInstance = SomeClass()

Getter 与 Setter

常量、变量、下标与属性的Getter和Setter的存取层级与他们所属类型的存取层级相同。

Setter的存取层级可以比对应的Getter存取层级限制更为严格,可以用来控制其读写权限。使用方式为在var或subscript关键字前,加上private(set)或internal(set)来指定限制更为严格的存取层级。以下是一个例子:


// 定义一个结构体 预设存取层级为 internal
struct TrackedString {
    // 将变量的 Setter 存取层级指定为 private
    private(set) var numberOfEdits = 0
    var value: String = "" {
        didSet {
            // 所以在结构体内部 是可以读写的
            numberOfEdits += 1
        }
    }
}

// 声明一个结构体的变量
var stringToEdit = TrackedString()

// 每修改一次 会经由属性观察器来将内部变量属性加一
stringToEdit.value = "字串修改次数会被记录"
stringToEdit.value += "每修改一次, numberOfEdits 数字会加一"
stringToEdit.value += "这行修改也会加一"

// 打印出:已修改了 3 次
print("已修改了 \(stringToEdit.numberOfEdits) 次")

你也可以在必要时为Getter与Setter显式指定存取层级,例子如下:


// 显式指定这个结构体的存取层级为 public
public struct TrackedString {
    // 结合 public 与 private(set)
    // 所以这时这个变量属性的 Setter 为 private
    // Getter 为 public
    public private(set) var numberOfEdits = 0
    public var value: String = "" {
        didSet {
            numberOfEdits += 1
        }
    }
    public init() {}
}

构造器

自定义构造器的存取层级限制不能比其所属类型的存取层级宽松,像是一个internal的类别,不能设置一个public的构造器。而唯一例外是,当构造器为必要构造器(required initializer)时,其存取层级必须与所属类型相同。

与函数或方法一样,构造器参数的存取层级限制也不能比构造器本身严格。像是一个internal的构造器,不能设置一个private的参数。

实例
在每个子类的 init() 方法前使用 required 关键字声明访问权限。


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

预设构造器

预设构造器的存取层级与所属类型的存取层级相同。除非当类型的存取层级为public,则预设构造器会被设置为internal,如果需要一个public的构造器,必须自己定义一个,如下:


public class SomeClass {
    public init() {}
}

结构体的成员逐一构造器

如果结构体中任意储存属性的存取层级为private,那么这个结构体预设的成员逐一构造器的存取层级就是private,否则就为internal。

如果需要在其他模组也可以使用这个结构体的成员逐一构造器,则必须自行定义一个public的成员逐一构造器。

协定

如果想为一个协定明确的指定存取层级,必须在定义此协定时指定。这样可以确保这个协定只能在适当的存取层级范围内被遵循。

协定中的每个功能都与该协定的存取层级相同,这样才能确保协定所有功能都可以被遵循此协定的类型存取。

如果你定义了一个public访问级别的协议,那么实现该协议提供的必要函数也会是public的访问级别。这一点不同于其他类型,比如,public访问级别的其他类型,他们成员的访问级别为internal。

实例


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

协定继承

从已存在的协定继承了一个新的协定时,这个新协定的存取层级不能比已存在协定的宽松。例如,定义一个public的协定时,不能继承自一个internal的协定。

协定一致性

一个类型可以遵循一个存取层级限制更为严格的协定,例如,你可以定义一个public的类型,并遵循一个internal的协定。

遵循了协定的类型的存取层级,会以类型本身与遵循的协定限制较严格的存取层级为准,如果一个public的类型,遵循了一个internal的协定,则在此类型作为符合协定的类型时,其存取层级也是internal。

当你让一个类型遵循某个协定并满足其所有要求后,你必须确保所有这些实作协定的部分,其存取层级不能比协定更为严格。例如一个public的类型,遵循了internal的协定,则实作协定的部份最严格只能到internal存取层级。

扩展

你可以在符合存取层级的情况下,扩展一个枚举、结构体或类别,这个扩展会与扩展的对象拥有一样的存取层级。例如,你扩展了一个public或internal类型,扩展中的成员则预设为internal,与原始类型中的成员一样。而当扩展了一个private类型时,扩展成员则预设为private。

或者,你也可以明确的指定扩展的存取层级,来让其内成员都预设成一样的存取层级。这个预设的存取层级仍可被个别成员所指定的存取层级覆盖。

经由扩展来遵循协定

如果你经由扩展来遵循协定,那你就不能显式的指定这个扩展的存取层级了。而这个协定本身的存取层级会变成预设的存取层级,且扩展中每个协定功能的实作也是一样的预设的存取层级。

泛型

泛型类型或泛型函数的存取层级由泛型类型或泛型函数本身与泛型的类型约束参数中限制最严格的来确定。

实例


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

类型别名

自定义的任何类型别名都会被当做不同的类型来做访问控制。类型别名不能拥有比原始类型限制更为宽松的存取层级。

一个public的类型,可以声明为private、fileprivate、internal或public的类型别名。一个private的类型,仅能声明为private的类型别名,不能声明为fileprivate、internal、public或open的类型别名。

这条规则也适用于为满足协议一致性而给相关类型命名别名的情况。

实例


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

Swift基础入门知识学习(25)-泛型-讲给你懂

高效阅读-事半功倍读书法-重点笔记-不长,都是干货

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/996544.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存