IOS面试题——Swift访问控制(14)

一 面试题汇总

  1. Swift访问控制有哪几种访问级别?分别是什么?
  2. 访问级别的使用准则?一个实体不可以被更低访问级别的实体定义
  3. 元组类型,泛型类型的访问级别如何确定?
  4. 类型的访问级别对其成员,嵌套类型的影响?
  5. 子类重写成员的访问级别有什么限制? ≥ 子类的访问级别,或者 ≥ 父类被重写成员的访问级别
  6. 协议定义的方法,枚举类型的case等是否能单独设置访问级别?
  7. public类的默认初始化器是internal级别。如果一个public类想在另一个模块调用编译生成的默认无参初始化器,必须显式提供public的无参初始化器

二 面试题解答(仅供参考)

2.1 Swift访问控制有哪几种访问级别?分别是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在 Swift 中,访问控制用于限制不同部分代码对其他部分代码的访问权限。
Swift 提供了五种不同的访问级别,用于指定实体(如类型、属性、方法等)的访问权限。
这些访问级别按照访问权限从高到低的顺序为:

1-Open: 最高级别的访问权限,仅适用于类和类的成员。
open 访问级别允许实体在定义模块外部被继承、重写(如果是类),以及在其他模块中被子类化。

2-Public: 指定实体可以被定义模块外的任何代码访问,包括其他模块。
公共实体可以被其他模块导入并使用,但不能被继承或重写(除非是在定义模块内)。

3-Internal: 默认的访问级别。指定实体可以被定义模块内的任何代码访问,
但不能被模块外的代码访问。内部实体对于模块的其它源文件是可见的,并且可以访问。

4-Fileprivate: 指定实体可以被定义在同一源文件中的其他实体访问,
但不能被同一模块中的其他源文件访问。文件私有实体对于整个模块来说是隐藏的,只能在同一源文件中访问。

5-Private: 最低级别的访问权限,指定实体只能被定义的封闭声明内访问。
私有实体对于整个模块和其他模块都是隐藏的,只能在定义它的作用域内访问。

这些访问级别可以应用于类、结构体、枚举、协议、属性、方法、初始化器、下标、以及其它类型的成员。
通过使用这些访问级别,开发者可以精确地控制代码的可见性和访问权限,从而提高代码的安全性和可维护性。

2.2 访问级别的使用准则?一个实体不可以被更低访问级别的实体定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
在 Swift 中,访问级别的使用准则有助于编写清晰、安全和易于维护的代码。
以下是一些常见的使用准则:

1-最小化访问权限:
应该尽可能地将实体的访问级别设置为最小化,即只暴露必要的接口给外部。
这有助于降低代码的耦合性,增加代码的可维护性和可理解性。

2-按需提供访问权限:
仅将必要的实体暴露给外部。
对于不需要被外部访问的实体,应该将其访问级别设置为尽可能高的级别,以减少不必要的暴露。

3-适当地使用公共访问级别:
对于需要被多个模块访问的实体,可以将其访问级别设置为 public。
但要注意,公共接口的设计应该经过深思熟虑,避免过度暴露细节或过于复杂。

4内部实现隐藏:
使用private 或fileprivate 访问级别来隐藏内部实现细节,只暴露外部需要的接口。
这有助于封装内部实现,防止外部代码直接访问或修改内部状态,提高代码的安全性和稳定性。

5-考虑模块边界:
在多模块项目中,要考虑不同模块之间的边界和依赖关系。
通过适当地设置访问级别,可以限制模块之间的耦合,提高代码的可维护性和可移植性。

6--使用 internal 默认访问级别:
对于不需要跨模块访问的实体,应该使用 internal 访问级别作为默认选项。
这有助于在模块内部提供适当的封装和抽象,同时避免不必要的暴露。

7-文档和注释:
对于公共接口和重要的内部实现,应该提供清晰的文档和注释,以便用户和其他开发者了解如何正确地使用和扩展代码。

遵循这些访问级别的使用准则,可以编写更加清晰、安全和易于维护的Swift代码,提高代码质量和开发效率。

2.3 元组类型,泛型类型的访问级别如何确定?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
在 Swift 中,元组类型和泛型类型的访问级别是由其中所有元素或成员的访问级别中的最低级别决定的。
换句话说,元组类型和泛型类型的访问级别不能高于其中最低级别的元素或成员的访问级别。

具体来说:

1-元组类型的访问级别:
如果一个元组中包含的元素的访问级别不一致,则元组类型的访问级别将与其中最低级别的元素相匹配。

internal let x = 10
public let y = "Hello"
let myTuple = (x, y) // myTuple 的访问级别为 internal

在上述示例中,myTuple 的访问级别为 internal,因为其中包含的元素 x 的访问级别为 internal,
而元素 y 的访问级别为 public,所以整个元组的访问级别将匹配其中最低级别的元素。

2-泛型类型的访问级别:
如果一个泛型类型的泛型参数或成员的访问级别不一致,则泛型类型的访问级别将与其中最低级别的泛型参数或成员相匹配。

internal struct MyStruct<T> {
var value: T
let x: Int
}

let myStruct = MyStruct(value: "Hello", x: 10) // myStruct 的访问级别为 internal

在上述示例中,MyStruct 的访问级别为 internal,因为它的泛型参数 T 的访问级别为 internal,
而成员 x 的访问级别也为 internal,所以整个结构体的访问级别将匹配其中最低级别的成员。

通过遵循这些规则,Swift 确保了元组类型和泛型类型的访问级别与其中成员的访问级别一致,
并且保持了代码的一致性和安全性。

2.4 类型的访问级别对其成员,嵌套类型的影响?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
在 Swift 中,类型的访问级别会影响到它的成员和嵌套类型的访问级别。
具体来说,类型的访问级别限制了它的成员和嵌套类型对外部代码的可见性。

以下是类型的访问级别对其成员和嵌套类型的影响:

1-成员的访问级别不得高于其所属类型的访问级别:
类型中的成员的访问级别不能高于类型本身的访问级别。
例如,如果一个结构体的访问级别是 internal,则它的成员的访问级别也必须是 internal,不能是 public 或 open。

2-嵌套类型的访问级别不得高于其所属类型的访问级别:
类型中的嵌套类型的访问级别也不能高于类型本身的访问级别。
例如,如果一个类的访问级别是 public,则它的嵌套类型的访问级别也可以是 public 或者 internal,但不能是 open。

3-成员和嵌套类型的访问级别可以低于其所属类型的访问级别:
类型中的成员和嵌套类型的访问级别可以低于类型本身的访问级别。
例如,一个结构体的访问级别是 public,但它的某个成员的访问级别可以是 internal
或 fileprivate,以便在模块内或文件内访问。

总的来说,类型的访问级别限制了其成员和嵌套类型的可见性范围,使得代码更加模块化、安全和易于维护。
通过合理设置类型和其成员的访问级别,可以有效地控制代码的访问权限,提高代码的可维护性和可重用性。

2.5 子类重写成员的访问级别有什么限制? ≥ 子类的访问级别,或者 ≥ 父类被重写成员的访问级别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
在 Swift 中,子类重写父类成员时有一些限制,
这些限制确保了重写成员的访问级别不会高于子类的访问级别,也不会高于被重写成员的访问级别。具体来说:

1-重写成员的访问级别不得高于子类的访问级别:
子类中重写父类的成员时,重写的成员的访问级别不能高于子类的访问级别。
例如,如果一个类的访问级别是 internal,那么它的子类的重写成员的访问级别也不能是 public。

2-重写成员的访问级别不得高于父类被重写成员的访问级别:
子类中重写父类的成员时,重写的成员的访问级别不能高于父类被重写成员的访问级别。
例如,如果父类中的一个成员的访问级别是 internal,那么子类中重写这个成员的访问级别也不能是 public。

综上所述,子类重写成员的访问级别必须遵循这两个限制,
以保证子类在使用继承的成员时不会突破其自身的访问权限。
这种设计可以确保代码的一致性和安全性,防止子类暴露出父类中不应该被公开的实现细节。

2.6 协议定义的方法,枚举类型的case等是否能单独设置访问级别?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
在 Swift 中,协议中定义的方法、属性、枚举类型的 case 等,是可以单独设置访问级别的。
这些实体可以具有与其所在的协议、枚举类型相同或不同的访问级别。

具体来说:

1-协议中定义的方法和属性:
可以单独设置它们的访问级别,不一定要与协议的访问级别相同。例如,可以在一个 internal 的协议中定义一个 public 的方法。

public protocol MyProtocol {
func myMethod()
}

internal struct MyClass: MyProtocol {
public func myMethod() {
// 实现...
}
}

2-枚举类型的 case:
类似地,枚举类型的 case 可以单独设置它们的访问级别,不一定要与枚举类型的访问级别相同。
例如,可以在一个 internal 的枚举类型中定义一个 public 的 case。

internal enum MyEnum {
case publicCase
case internalCase
}

let enumValue: MyEnum = .publicCase

这种灵活性允许开发者根据具体需求来设置各种实体的访问级别,从而更好地控制代码的可见性和访问权限

2.7 public类的默认初始化器是internal级别。如果一个public类想在另一个模块调用编译生成的默认无参初始化器,必须显式提供public的无参初始化器

1
2
3
4
5
6
在 Swift 中,如果一个 public 类没有任何显式定义的初始化器,编译器会为该类生成一个默认的无参初始化器。
这个默认的无参初始化器的访问级别将会是 internal,而不是 public。
这意味着在另一个模块中,无法直接访问这个默认的无参初始化器。

为了使得在另一个模块中可以调用编译生成的默认无参初始化器,
你需要在该类中显式提供一个 public 的无参初始化器。这样,该初始化器就可以被其他模块所访问

三 参考

  • 简书—Swift访问控制