这是Swift编程思维的系列文章:

Swift 编程思维(一)面向协议编程

Swift 编程思维(二)面向函数式编程

Swift 编程思维(三)面向泛型编程

[Swift 编程思维(四)面向呼应式编程(待完结)]

Swift 编程思维(五)链式表达结构

了解泛型

泛型的界说

在Swift语言中,泛型是一种编程技能,它答应你编写灵敏、可重用的函数和类型,能够工作于任何类型。泛型的主要好处是它能够协助你防止重复代码,并用一种清晰和笼统的方式来表达代码的意图。

泛型的占位类型,能够是 T,也能够是U,彻底由您决议,运用一个由意义的占位符,更能表达意义。例如:体系对数组的界说 struct Array<Element>

在某场技能活动中,需求办理会场中的观众,每人有必要观看两个小时才能够离场(先进先出,无法提早离场)。

会场分为三个会场,分别有以下要求:

Swift编程思维(三)  - 面向泛型编程

在主会场A中,因为准备充分,每个进场的观众发放参会证,运用参会证上的号码进场。

运用 Stack 办理观众的进场和离场。Stack经过push办法记录进场的用户号码,经过pop办法移除离场的用户。

struct Stack {
 var items = [Int]()
 mutating func push(_ item: Int) {
   items.append(item)
 }
 mutating func pop() {
   return items.removeFirst()
 }
}

泛型函数

活动火爆,观众来的很多,主办方又开展了分会场B。因为时刻有限,没有制作进场证,每个进场的观众的凭身份证(名字)进场。为了能让Stack不仅能够办理会场A,一起也能够办理会场B。我们进行了如下修正:

struct Stack {
 var items = [Any]()
 mutating func push<T>(_ item: T) {
   items.append(item)
 }
 mutating func pop() {
   return items.removeFirst()
 }
}

泛型函数可适用于恣意类型。泛型函数运用 占位符 类型名(这儿叫做 T ),而不是实际类型名(例如 IntStringDouble),占位符 类型名并不关怀 T 详细的类型,只要在运用的时分才确认类型。

泛型类型

一个球被踢入了会场,一个气球被吹入了会场,Stack要不要记录?

stock能够push恣意类型,开发人员头大,难以办理。

stock.push(1)
stock.push("1123")
stock.push(UIView())

技能大佬说: 运用泛型类型。会场对应的Stack清晰类型。

struct Stack<Element> {
 var items = [Element]()
 mutating func push(_ item: Element) {
   items.append(item)
 }
 mutating func pop() {
   return items.removeFirst()
 }
}

主会场A中要求: 只能进入有号码的观众。

var a = Stack<Int>()
a.push(1)
a.push(2)

分会场B中要求: 只能进入运用称号的观众。

var a = Stack<Sting>()
a.push("小明")
a.push("大黄")

完美的结局了问题。

泛型扩展

领导来观察,问最后一个进入的是谁?

开发人员慌了,开发的时分没有规划,怎么办?

技能大佬说: 别慌,运用泛型扩展。

extensionStack{
 varcount:Int{
   returnitems.count
 }
}

当对泛型类型进行扩展时,原始类型界说中声明的类型参数列表在扩展中能够直接运用,而且这些来自原始类型中的参数称号会被用作原始界说中类型参数的引证。

泛型束缚

主办方又开了一个会场C,这个会场主题是:关爱女人(只答应女人进入)。

开发人员慌了,怎么办?怎么办?

技能大佬淡定的说: 运用泛型束缚,只答应女人(完成了Womenable协议的Element)进来就好了。

// 女人的协议
protocol Womenalbe { }
​
struct Stack<Element: Womenable> {
 var items = [Element]()
 mutating func push(_ item: Element) {
   items.append(item)
 }
 mutating func pop() {
   return items.removeFirst()
 }
}

在一个类型参数名后边放置一个类名或许协议名,并用冒号进行分隔,来界说类型束缚。下面将展现泛型函数束缚的根本语法(与泛型类型的语法相同)。下面这个函数有两个类型参数,第一个类型参数 T 有必要是 SomeClass 子类;第二个类型参数 U 有必要契合 SomeProtocol 协议。

funcsomeFunction<T:SomeClass,U:SomeProtocol>(someT:T,someU:U){
 // 这儿是泛型函数的函数体部分
}

泛型与协议

A和B会场中的观众定见很大: 我要上厕所,不要约束我!!!

主办方在正常脱离通道旁边,开通了暂时通道:能够不到时刻就能够离场。

可是女人会场有厕所,为了保证参会作用,暂时不答应通行,有人经过给予提示:场内有厕所,暂不支撑提早离场。

开发人员又犯难了,一个办法怎么支撑两种完成?

会场A和会场B中,仍然采用先进先出的办理办法。

mutating func pop() -> Element {
 return items.removeFirst()
}

会场C中,支撑提早离场。

mutating func pop(_ item: Element) {
 print("场内有厕所,暂不支撑提早离场")
}

经过协议界说能力

技能大佬也陷入了考虑……

只见先完成了Container协议,他说将曾经的完成抽离出来:

  • items: 界说为会场中的人
  • last:最后一个进入会场的人
  • push:记录进入会场的人的办法
  • pop:记录脱离会场的人的办法
protocol Container {
 associatedtype Element
 var items: [Element] { get set }
 var last: Element? { get }
 mutating func push(_ item: Element)
 mutating func pop()
 mutating func pop(with item: Element)
}

在这三个会场中相同的完成的一致完成:

extension Container {
 mutating func push(_ item: Element) {
   items.append(item)
 }
 
 mutating func pop() {
   items.removeFirst()
 }
 
 var last: Element? {
   return items.isEmpty ? nil : items[items.count - 1]
 }
}

泛型结合协议运用

三个会场的差异性的完成:

struct StackA: Container {
 var items: [Int] = []
 mutating func pop(with item: Int) {
   items.removeAll { $0 == item }
 }
}
​
struct StackB: Container {
 var items: [String] = []
 mutating func pop(with item: String) {
   items.removeAll { $0 == item }
 }
}
​
struct StackC<T: Womenalbe>: Container {
 
 var items: [T] = []
 mutating func pop(with item: T) {
   print("场内有厕所,暂不支撑提早离场")
 }
}

给相关类型增加束缚

场馆C中的观众十分不满: 为什么要区别对待? 我们为什么不能离场?

开发人员心想: 这还不简单? 参考大佬的完成,改一下就行了。

struct StackC<T: Womenalbe>: Container {
 var items: [T] = []
 mutating func pop(with item: T) {
   items.removeAll { $0 == item }
 }
}

❌ 报错了: Referencing operator function '==' on 'Equatable' requires that 'T' conform to 'Equatable'

‍,仍是要找技能大佬处理。

大佬一看就知道了我那天: T 类型需求遵守 Equatable,才能够进行比较。

protocol Womenalbe: Equatable { }

让 Womenable 也遵从 Equatable 协议就好了。

此刻的 T 类型:不仅要是Womenalbe,还要是Equatable,才能够提早离场。

具有泛型 Where 子句的扩展

主办方发现C场馆中,有一些带孩子的母亲有提早离场的需求。要求:在C场馆中带孩子的女人能够提早离场。

protocol Childable { }
extension StackC where T: Childable {
 mutating func pop(with item: T) {
   items.removeAll { $0 == item }
 }
}

面向泛型编程

经过上面的学习,我们对泛型有了必定的了解,那么运用泛型进行编程相信也能够胜任了。

面向泛型编程是一种编程范式,强调代码的通用性和复用性。

  1. 通用性:经过泛型编写的代码能够适用于多种数据类型。
  2. 类型安全:泛型坚持类型安全,削减类型转化和过错。
  3. 可复用性:泛型进步代码复用性,削减重复代码。
  4. 笼统编程:经过界说泛型和协议,能够更笼统地处理问题,更专注于逻辑而非类型特定的细节。

总结:Swift 的泛型和面向泛型编程思维,为 Swift 编程供给了高效、安全和易于保护的代码写法。

1. 通用性

泛型让你能够写出适用于任何类型的代码。这削减了对特定类型的依赖,然后增加了代码的灃用范围。

比如

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
 let temporaryA = a
 a = b
 b = temporaryA
}

这个函数能够交换任何类型的两个值,无论是整数、字符串仍是自界说类型。

2. 类型安全

在泛型编程中,类型是在编译时确认的,这保证了类型的正确性和安全性。

比如

struct Stack<Element> {
 var items = [Element]()
 mutating func push(_ item: Element) {
   items.append(item)
 }
 mutating func pop() -> Element {
   return items.removeLast()
 }
}

在这个 Stack 示例中,你能够为栈指定存储的元素类型(如 IntString),并保证只要正确的类型能被增加到栈中。

3. 可复用性

经过泛型,你能够写出高度复用的代码,削减重复和冗余。

比如

func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
 for (index, value) in array.enumerated() {
   if value == valueToFind {
     return index
   }
 }
 return nil
}

这个函数可用于查找任何遵从 Equatable 协议类型的数组中的元素,无论是数字、字符串仍是其他自界说可比较类型。

4. 笼统编程

泛型编程鼓舞从详细完成中笼统出来,重视逻辑而非详细类型。

比如

protocol Container {
 associatedtype Item
 mutating func append(_ item: Item)
 var count: Int { get }
 subscript(i: Int) -> Item { get }
}

这个 Container 协议界说了一个容器应有的根本行为,而不关怀容器中详细存储的元素类型。任何遵从此协议的类型都需求完成这些根本行为,供给了一种高层次的笼统。

这些比如展现了如何经过泛型来完成面向泛型编程的四个主要特点,体现了 Swift 泛型编程的灵敏性和强大功用。