IOS面试题——Swift 泛型,关联类型,协议类型,不透明类型(6)

一 面试题汇总

  1. 泛型有什么作用?类型参数化
  2. 什么是关联类型?有什么作用?给协议中用到的类型定义一个占位名称
  3. 什么是协议类型,协议类型能否作为函数返回值?
  4. 泛型类型如何约束?
  5. 什么是不透明类型?some限制只能返回一种类型

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

2.1 泛型有什么作用?类型参数化

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
32
33
34
35
36
37
38
39
泛型(Generics)是一种编程技术,它允许你编写具有一般性的、可复用的代码,
不仅可以应用于特定类型,而且可以应用于任意类型。
泛型允许你将类型参数化,从而使函数、方法、类和结构体能够处理任意类型的数据,而不是特定类型。

泛型的作用主要体现在以下几个方面:

1-代码重用: 使用泛型可以编写通用的代码,使得代码可以用于不同类型的数据,
从而减少了重复编写代码的工作量。
2-类型安全: 使用泛型可以在编译时进行类型检查,从而提高了代码的类型安全性。
编译器可以检查泛型代码是否符合类型约束,避免了在运行时出现类型错误的可能性。
3-灵活性: 泛型使得代码更加灵活,可以处理各种不同类型的数据,
而不需要针对每种类型编写特定的代码。这使得代码更易于维护和扩展。
4-抽象: 泛型可以将算法和数据结构与具体的数据类型分离开来,使得代码更加抽象和通用化。
这样一来,代码的逻辑结构更清晰,易于理解和修改。

示例:
一个简单的示例是 Swift 标准库中的 Array 类型。
Array 类型是一个泛型类型,它可以存储任意类型的元素。
通过使用泛型,可以将 Array 类型定义为一个通用的、可复用的数据结构,可以用于存储各种类型的数据。

// 定义一个泛型函数,用于交换两个值的位置
func swap<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}

var x = 10
var y = 20
swap(&x, &y)
print("x = \(x), y = \(y)") // 输出:x = 20, y = 10

var str1 = "hello"
var str2 = "world"
swap(&str1, &str2)
print("str1 = \(str1), str2 = \(str2)") // 输出:str1 = world, str2 = hello

在这个示例中,swap 函数是一个泛型函数,它可以交换任意类型的值的位置。
通过使用泛型类型参数 T,函数可以处理不同类型的数据,使得代码更加灵活和通用。

2.2 什么是关联类型?有什么作用?给协议中用到的类型定义一个占位名称

1
2
3
4
5
6
7
8
9
10
关联类型(Associated Types)是一种在协议中定义占位名称的方式,这些占位名称代表着实际的类型。
在协议中使用关联类型可以使协议更加灵活,允许遵循该协议的类型来决定实际的类型。

关联类型允许协议中使用的类型在遵循协议时动态确定,而不是在协议中固定下来。
这样一来,协议可以适应不同的类型,从而增加了协议的通用性和可复用性。

作用:
灵活性: 关联类型使协议更加灵活,允许遵循协议的类型来决定具体的类型实现。
代码复用: 关联类型可以使相似的协议共享一组相关的类型定义,从而提高代码的复用性和可读性。
泛型编程: 关联类型与泛型编程密切相关,可以使协议与特定的类型解耦,使得协议更加通用。

2.3 什么是协议类型,协议类型能否作为函数返回值?

1
2
3
4
5
6
7
8
协议类型指的是一个协议,而不是实际的类型。
在 Swift 中,可以使用协议类型来表示满足某个协议的任意类型。
这种方式可以增加代码的灵活性,使得函数、方法和属性可以接受任何遵循了特定协议的类型作为参数或者返回值。

作为函数返回值:
协议类型可以作为函数的返回值类型。
这意味着函数可以返回任何满足指定协议的类型的实例。
这种方式使得函数的返回值可以根据具体的实现动态确定,增加了函数的灵活性。

2.4 泛型类型如何约束?

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
在 Swift 中,可以通过类型约束(Type Constraints)来限制泛型类型参数的类型范围,
从而增加代码的类型安全性。类型约束允许你指定泛型类型参数必须满足的条件,
例如必须是某个特定类型、遵循某个协议等。

类型约束可以在泛型函数、泛型方法和泛型类型中使用。下面是一些常见的类型约束:

1. 指定类型约束:

func process<T: Equatable>(value: T) {
// T 必须遵循 Equatable 协议
// 在函数中可以使用 == 操作符来比较值
}

process(value: 10) // 合法,Int 遵循 Equatable 协议
process(value: "Hello") // 合法,String 遵循 Equatable 协议

2. 类型约束与协议:

protocol Numeric {
static func +(lhs: Self, rhs: Self) -> Self
}

func sum<T: Numeric>(a: T, b: T) -> T {
return a + b // T 必须遵循 Numeric 协议
}

sum(a: 1, b: 2) // 合法,Int 遵循 Numeric 协议
sum(a: 3.5, b: 4.5) // 合法,Double 遵循 Numeric 协议

3. 类型约束与类:

func process<T: SomeClass>(value: T) {
// T 必须是 SomeClass 或其子类的类型
}

process(value: SomeClass()) // 合法,SomeClass 的实例
process(value: Subclass()) // 合法,Subclass 是 SomeClass 的子类

4. 多重约束:

func process<T: SomeProtocol & AnotherProtocol>(value: T) {
// T 必须同时遵循 SomeProtocol 和 AnotherProtocol
}

struct MyStruct: SomeProtocol, AnotherProtocol { }

process(value: MyStruct()) // 合法,MyStruct 同时遵循 SomeProtocol 和 AnotherProtocol

类型约束可以确保泛型类型参数满足特定的条件,从而在编译时提供类型安全性。
这样可以避免不必要的类型转换和运行时错误,并且提高代码的可读性和可维护性。

2.5 什么是不透明类型?some限制只能返回一种类型

1
2
3
4
5
6
7
8
不透明类型(Opaque Types)是 Swift 5.1 新增的特性,
它允许你隐藏实际类型的细节,而只提供抽象的类型和行为。
使用不透明类型可以增加代码的安全性和抽象性,同时也提高了代码的可读性和可维护性。

在 Swift 中,some 关键字用于声明不透明类型。
当你使用 some 关键字来定义函数的返回类型或属性的类型时,
你实际上告诉编译器:“返回的类型或属性的类型是某个遵循了特定协议的类型,
但是具体的类型我并不关心,只要满足协议即可”。

三 参考

  • 简书—Swift 泛型,关联类型,协议类型,不透明类型