Swift-30-高级编程-类型扩展和协议扩展

news/2024/9/23 21:02:24/

类型扩展

Swift提供一个叫扩展(extension)的特性,该特性就是为这种情况设计的。扩展能让你给已有的类型添加功能,可以用来扩展结构体、枚举和类,比如:

  1. 添加计算属性
  2. 添加新的初始化方法
  3. 添加协议实现
  4. 添加新方法
  5. 添加嵌入类型(枚举和结构体)

语法格式

extension TypeName {}

比如,为系统的Double对象添加一个mph方法,就可以用下列的代码实现

swift">extension Double {var mph: Double { return self }
}

扩展系统类库

swift">// 给Double起个别名
typealias Velocity = Double//添加两个计算属性
extension Velocity {var kph: Velocity { return self * 1.60934 }var mph: Velocity { return self }
}

扩展自定义类型

本例子中会以自定义的Car结构体为例进行说明,如何扩展自定义的类型的。

定义测试数据

定义测试结构体

  • 自定义结构体
swift">// 自定义类型
struct Car {let make: Stringlet model: Stringlet year: Intlet color: Stringlet nickname: Stringvar gasLevel: Double { //可变的存储属性,附带了观察者实现willSet {precondition(newValue <= 1.0 && newValue >= 0, "New value must be between 0 and 1.")}}
}

定义测试协议

在自定义协议添加三个计算属性

swift">protocol Vehicle {var topSpeed: Velocity { get } var numberOfDoors: Int { get }var hasFlatbed: Bool { get }
}

扩展实现

用扩展方法实现协议

下面的代码其实就是协议实现的另一种写法

swift">// 添加Vehicle协议实现
extension Car: Vehicle {var topSpeed: Velocity { return 180 }var numberOfDoors: Int { return 4 }var hasFlatbed: Bool { return false }
}

增强自定义类型功能

  • 添加初始化方法:前面提到过,如果结构体没有初始化方法的话,默认会自带一个成员初始化方法。如果想给结构体加一个新的初始化方法,同时又不想失去成员初始化方法,那就可以用扩展给结构体添加初始化方法。在扩展中为Car添加一个初始化方法,如下列代码清单所示。

Car的成员初始化方法之所以能保留下来,是因为新初始化方法是在扩展中定义和实现的,这个模式很有用。如果用自定义初始化方法的方式会覆盖掉默认的初始化方法

swift">//扩展初始化方法
extension Car {init(make: String, model: String, year: Int) {self.init(make: make,model: model,year: year,color: "Black",nickname: "N/A",gasLevel: 1.0)}
} //测试代码
var c = Car(make: "Ford", model: "Fusion", year: 2013)
  • 添加嵌套类型和扩展:Swift扩展还可以给已有的类型添加嵌套类型,比如下例给Car添加一个枚举来协助汽车的分类。
swift">//添加嵌入类型
extension Car {enum Kind { //添加的嵌套类型case coupe, sedan}var kind: Kind { //添加一个名为Kind的计算属性if numberOfDoors == 2 {return .coupe} else {return .sedan}}
} //测试代码
var c = Car(make: "Ford", model: "Fusion", year: 2013)
c.Kind
  • 添加函数方法:用扩展给已有类型添加新的函数,下例中就给Car类型添加了两个新的方法。
swift">//添加函数
extension Car {mutating func emptyGas(by amount: Double) {//precondition是一个框架方法precondition(amount <= 1 && amount > 0, "Amount to remove must be between 0 and 1.")gasLevel -= amount}mutating func fillGas() {gasLevel = 1.0}
}//测试代码
var c = Car(make: "Ford", model: "Fusion", year: 2013)
c.emptyGas(by: 0.3)
c.gasLevel
c.fillGas()
c.gasLevel

协议扩展

OOP中的一个强大但也是一个缺陷功能就是继承,尤其过深的继承层次很容易让代码充满难以理解的类。Swift为设计可重用、可组合的类型带来了新机会:不用类和继承,而是用协议和泛型。即便使用值类型,协议也能解决OOP中继承能解决的问题。协议扩展(protocol extension)是使得这种设计成为可能的强大工具。语法结构如下:

协议扩展:相当于一种变相的继承实现,对应的是objective-C中的概念。

基础语法

语法结构:protocol Exercise: CustomStringConvertible

定义自定义的测试协议

Exercise是一个自定义的协议,CustomStringConvertible是一个系统协议

swift">protocol Exercise: CustomStringConvertible {var name: String { get }var caloriesBurned: Double { get }var minutes: Double { get }
}

扩展协议实现

扩展协议添加方法

在上面扩展的CustomStringConvertible协议中包含了一个名为description的函数,必须要实现,这段代码也可以写在上面的实现了,但为了演示协议的扩展,特意分出来一段代码实现,如下:

swift">extension Exercise {var description: String {return "Exercise(\(name), burned \(caloriesBurned) calories in \(minutes) minutes)"}
}

实现自定义的协议

自定义两个结构体,实现自定义的协议

swift">//定义结构体EllipticalWorkout,并扩展Exercise协议
struct EllipticalWorkout: Exercise {let name = "Elliptical Workout"let title = "Workout using the Go Fast Elliptical Trainer 3000"let caloriesBurned: Doublelet minutes: Double
}//Exercise(Elliptical Workout, burned 335.0 calories in 30.0 minutes)
let ellipticalWorkout = EllipticalWorkout(caloriesBurned: 335, minutes: 30)//定义结构体TreadmillWorkout,并扩展Exercise协议
struct TreadmillWorkout: Exercise {let name = "Treadmill Workout"let caloriesBurned: Doublelet minutes: Doublelet laps: Double
}//可以二次扩展,这个方法会覆盖掉原来 Exercise 中的实现
extension TreadmillWorkout {var description: String {return "Treadmill(\(caloriesBurned) calories and \(laps) laps in \(minutes) minutes)"}
}

测试自定义的协议

//Treadmill(350.0 calories and 10.5 laps in 25.0 minutes)
let ellipticalWorkout = EllipticalWorkout(caloriesBurned: 335, minutes: 30)//Treadmill(350.0 calories and 10.5 laps in 25.0 minutes)
let runningWorkout = TreadmillWorkout(caloriesBurned: 350, minutes: 25, laps: 10.5)

扩展协议

添加方法

在原来的Exercise中添加新的方法。

swift">extension Exercise {var caloriesBurnedPerMinute: Double {return caloriesBurned / minutes}
}
//11.166666666666666
let ellipticalWorkout = EllipticalWorkout(caloriesBurned: 335, minutes: 30)
print(ellipticalWorkout.caloriesBurnedPerMinute)//14.0
let runningWorkout = TreadmillWorkout(caloriesBurned: 350, minutes: 25, laps: 10.5)
print(runningWorkout.caloriesBurnedPerMinute)

添加扩展的限制条件

用where子语句可以限制协议扩展的生效范围,比如Sequence就是一个系统的协议,用于实现集合迭代时的迭代对象的集合。如果要限制生效范围就可以用where语句来实现。

比如下例中,扩展Sequence协议,但只对元素类型为Exercise的序列有效。

swift">extension Sequence where Iterator.Element == Exercise {//添加新的方法func totalCaloriesBurned() -> Double { var total: Double = 0for exercise in self { //self值为 Sequencetotal += exercise.caloriesBurned}return total}
}//创建测试数组,输出 ~~ 685.0
let mondayWorkout: [Exercise] = [ellipticalWorkout, runningWorkout]
print(mondayWorkout.totalCaloriesBurned())

添加默认实现

上述实现了给协议添加属性和方法功能,还可以利用协议扩展提供协议自身需求的默认实现。基实这段代码在之前出现了,为了突出功能,再详细解释下。通过这种实现那么所有符合Exercise协议的结构体默认就添加了此实现。

swift">extension Exercise {var description: String {return "Exercise(\(name), burned \(caloriesBurned) calories in \(minutes) minutes)"}
}

如果在符合协议的类型中定义了同名的方法,则上述的默认实现就会被覆盖。

swift">//定义结构体TreadmillWorkout,并扩展Exercise协议
struct TreadmillWorkout: Exercise {let name = "Treadmill Workout"let caloriesBurned: Doublelet minutes: Doublelet laps: Double
}//覆盖了 extension Exercise 的实现
extension TreadmillWorkout {var description: String {return "Treadmill(\(caloriesBurned) calories and \(laps) laps in \(minutes) minutes)"}
}

http://www.ppmy.cn/news/1435262.html

相关文章

s AbortController 接口取消多次请求 取消上次请求

AbortController 是一个 JavaScript API&#xff0c;它允许您在客户端中止一个或多个 Fetch 请求。这个 API 是 Fetch API 的一部分&#xff0c;并且与 AbortSignal 对象一起使用&#xff0c;以提供一种机制来控制请求的生命周期。 以下是 AbortController 的基本用法&#xf…

【数据结构】图(Graph)

文章目录 概念图的存储方式邻接矩阵邻接矩阵表示法邻接矩阵表示法的特点 邻接表邻接表表示法邻接表表示法的特点邻接表表示法的定义与实现查找插入删除其它构造函数析构函数创建图输出图 图的遍历深度优先遍历&#xff08;DFS&#xff09;广度优先遍历 图的连接分量和生成树生成…

《深入浅出.NET框架设计与实现》笔记6.1——ASP.NET Core应用程序多种运行模式之一——自宿主(Self-Hosting)

ASP.NET Core应用程序可以在多种运行模式下运行&#xff0c;包括自宿主&#xff08;Self-Hosting&#xff09;、IIS服务承载、桌面应用程序、服务承载。 因此选择和时的模式很重要。 自宿主&#xff08;Self-Hosting&#xff09; 自宿主是指 ASP.NET Core 应用程序独立运行&a…

「ChatGPT」掀起新一轮AI热潮!超越GPT-4 Turbo,商汤日日新大升级!

目录 拳打 GPT-4 Turbo &#xff0c;脚踢 DALLE 3 端侧大模型&#xff0c;唯快不破 AI 应用落地需要一个即插即用的大模型超市 并不存在 AI 这个行业&#xff0c;只有 AI行业&#xff0c;强调 AI 需要与传统产业合作&#xff0c;这种关系是结合与赋能&#xff0c;而不是颠覆…

【VueUse】重新定义状态管理在 Vue 中的体验

在 Vue 生态系统中&#xff0c;状态管理一直是开发者们关注的焦点之一。而随着 VueUse 的出现&#xff0c;我们迎来了一种全新的方式来处理状态管理&#xff0c;它让我们能够以更简单、更灵活的方式来管理应用程序的状态。 在本文中我们将深入探讨 VueUse 中与状态管理相关的内…

Chrome 侧边栏开发示例

前言 最近做项目&#xff0c;需要开发浏览器扩展&#xff0c;但是考虑页面布局兼容性问题&#xff0c;使用了Chrome114开始的侧边栏&#xff0c;浏览器自带的能力毕竟不会出现兼容性问题&#xff0c;不过Chrome123开始&#xff0c;侧边栏居然又可以选择固定右侧扩展栏了&#…

uniapp h5文件流下载pdf文件

今天遇到一个需求就是后端返回一个文件流&#xff0c;前端需要用户点击以后下载&#xff0c;那么怎么去做呢 请看下面代码 DownLoadDoc() {let url doc/api/downloadDocFulltext?id this.id //接口路径uni.request({url:uni.getStorageSync(baseUrl)url, //域名拼接response…

事务-Java Spring

在Java Spring框架中&#xff0c;事务管理是非常重要的部分。事务允许我们将多个数据库操作组合成一个单一的工作单元。这样&#xff0c;要么所有操作都成功执行&#xff0c;要么所有操作都回滚&#xff08;撤销&#xff09;。Spring通过声明式事务管理和编程式事务管理两种方式…