Files
knowledge-kit/Chapter1 - iOS/1.120.md
2024-04-27 13:01:58 +08:00

203 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Swift 访问控制
## 访问控制
在访问权限控制这块Swift 提供了5个不同的访问级别(以下是从高到低排列, 实体指被访问级别修饰的内容)
- open允许在定义实体的模块、其他模块中访问允许其他模块进行继承、重写open只能用在类、类成员上)
- public允许在定义实体的模块、其他模块中访问不允许其他模块进行继承、重写
- internal只允许在定义实体的模块中访问不允许在其他模块中访问
- fileprivate只允许在定义实体的源文件中访问
- private只允许在定义实体的封闭声明中访问
绝大部分实现默认都是 internal
## 使用准则
各种 case 如下:
- 变量类型 >= 变量
```swift
fileprivate class Person {
func sayHi() {}
}
internal var p:Person = Person() // Variable cannot be declared internal because its type uses a fileprivate type
class Dog {
func sepak() {
p.sayHi()
}
}
```
编译器报错:`Variable cannot be declared internal because its type uses a fileprivate type`。 访问权限前后冲突了。变量 p 被声明为 internal也就是其他类中也可以使用该对象。但 Person 类被 fileprivate 修饰了,也就是只可以在定义该类的文件中使用。所以会冲突
- 参数类型、返回值类型 >= 函数
- 父类 >= 子类
- 父协议 >= 子协议
- 原类型 >= typealias
```swift
class Person {}
fileprivate typealias People = Person // 编译通过
public typealias People = Person // Type alias cannot be declared public because its underlying type uses an internal type
```
`Person` 默认是 internal所以 typealias 的权限修饰符需要小于等于 internal。
- 原始值类型、关联值类型 >= 枚举类型
```swift
// 编译通过
typealias OwnInt = Int
typealias OwnString = String
internal enum DataKind {
case int(OwnInt)
case string(OwnString)
}
// 编译失败
fileprivate typealias OwnInt = Int
fileprivate typealias OwnString = String
public enum DataKind {
case int(OwnInt) // Enum case in a public enum uses a fileprivate type
case string(OwnString) // Enum case in a public enum uses a fileprivate type
}
```
- 定义类型 A 时用到的其他类型 >= 类型A
- 元祖类型:元祖类型的访问级别是所有成员类型最低的那个(木桶原理)
```swift
internal struct Dog {}
fileprivate class Person {}
internal var data1:(Dog, Person) // Error:Variable cannot be declared internal because its type uses a fileprivate type
fileprivate var data1:(Dog, Person)
private var data2:(Dog, Person)
```
看似规则比较多,实际上就是对「一个实体不可以被更低访问级别的实体定义」的解释。
类型的访问级别会影响成员(属性、方法、初始化器、下标)、嵌套类型的默认访问级别
- 一般情况下,类型为 fileprivate、private那么成员、嵌套类型默认也是 private、fileprivate
- 一般情况下,类型为 internal、public那么成员、嵌套类型默认也是 internal
测试:
```swift
// 编译不通过
class TestClass {
private class Person {}
fileprivate class Student : Person {} // Class cannot be declared fileprivate because its superclass is private
}
// 编译通过
private class Person {}
fileprivate class Student : Person {}
```
当 fileprivate、private 都写在文件的全局作用域时,访问权限是一样的。
## getter、setter
getter、setter 默认自动接收他们所属换的访问级别
可以给 setter 单独设置一个比 getter 权限更低的访问级别,用以限制写权限
```swift
private(set) var num = 10
class Person {
private(set) var age = 0
init(age: Int = 0) {
self.age = age
}
public var weight: Int {
set {}
get { 10 }
}
}
var p = Person(age: 10)
//print(p.age)
//p.age = 20
print(num)
num = 20
print(num)
```
## 初始化器
- 如果一个 public 类,想在另一个模块调用编译生成的默认无参初始化器,必须显示提供 public 的无参初始化器(因为 public 类的默认初始化器是 internal 级别的)
```swift
// APM.dylib
public class APMManager {
public init () {
}
}
// 另一个模块(动态库/静态库/主工程)
var manager = APMManager()
```
## 枚举类型
不能给 enum 的 case 单独设置访问级别,每个 case 自动对齐 enum 的访问级别
- public 的 enum各个 case 也是 public
## 协议
协议中定义的要求自动接收协议的访问级别,不能单独设置访问级别
- public 定义的协议,各个属性、方法也是 public 级别
协议实现的访问级别 >= 类型的访问级别(协议的访问级别)
```swift
// 编译通过
public protocol Runnable {
func run()
}
internal class Person : Runnable {
internal func run() { }
}
// 编译失败
public protocol Runnable {
func run()
}
public class Person : Runnable {
internal func run() { } // Method 'run()' must be declared public because it matches a requirement in public protocol 'Runnable'
}
```