Swift5.4 语言指南(二十二) 扩展
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/9739746.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
扩展将新功能添加到现有的类,结构,枚举或协议类型。这包括扩展您无法访问原始源代码的类型的能力(称为追溯建模)。扩展与Objective-C中的类别相似。(与Objective-C类别不同,Swift扩展没有名称。)
Swift中的扩展可以:
- 添加计算实例属性和计算类型属性
- 定义实例方法和类型方法
- 提供新的初始化器
- 定义下标
- 定义和使用新的嵌套类型
- 使现有类型符合协议
在Swift中,您甚至可以扩展协议以提供其要求的实现,或添加符合类型可以利用的其他功能。有关更多详细信息,请参见协议扩展。
笔记
扩展可以为类型添加新功能,但不能覆盖现有功能。
扩展语法
使用extension
关键字声明扩展名:
- extension SomeType {
- // new functionality to add to SomeType goes here
- }
扩展可以扩展现有类型,以使其采用一种或多种协议。要添加协议一致性,可以使用与为类或结构编写协议名称相同的方式来编写协议名称:
- extension SomeType: SomeProtocol, AnotherProtocol {
- // implementation of protocol requirements goes here
- }
在以扩展方式添加协议一致性中描述了以这种方式添加协议一致性。
扩展可用于扩展现有的通用类型,如扩展通用类型中所述。您还可以扩展泛型类型以有条件地添加功能,如使用Generic Where子句的扩展中所述。
笔记
如果定义扩展以将新功能添加到现有类型,则新功能将在该类型的所有现有实例上可用,即使它们是在定义扩展之前创建的。
计算属性
扩展可以将计算的实例属性和计算的类型属性添加到现有类型。本示例向Swift的内置Double
类型添加了五个计算实例属性,为使用距离单位提供基本支持:
- extension Double {
- var km: Double { return self * 1_000.0 }
- var m: Double { return self }
- var cm: Double { return self / 100.0 }
- var mm: Double { return self / 1_000.0 }
- var ft: Double { return self / 3.28084 }
- }
- let oneInch = 25.4.mm
- print("One inch is \(oneInch) meters")
- // Prints "One inch is 0.0254 meters"
- let threeFeet = 3.ft
- print("Three feet is \(threeFeet) meters")
- // Prints "Three feet is 0.914399970739201 meters"
这些计算的属性表示Double
应将值视为某个长度单位。尽管它们被实现为计算属性,但是可以使用点语法将这些属性的名称附加到浮点文字值上,以作为使用该文字值执行距离转换的一种方式。
在该示例中,Double
值1.0
“ 1”被认为代表“一米”。这就是为什么m
计算的属性返回self
的原因-将该表达式1.m
视为计算的Double
值1.0
。
其他单位需要某种转换,以米为单位来表示。一公里等于1,000米,因此km
计算出的属性将值乘以1_000.00
以转换成以米表示的数字。同样,一米中有3.28084英尺,因此,ft
计算出的属性会将基础Double
值除以3.28084
,以将其从英尺转换为米。
这些属性是只读的计算属性,因此get
为了简洁起见,它们不使用关键字来表示。它们的返回值类型为Double
,并且可以在Double
接受a的数学计算中使用:
- let aMarathon = 42.km + 195.m
- print("A marathon is \(aMarathon) meters long")
- // Prints "A marathon is 42195.0 meters long"
笔记
扩展可以添加新的计算属性,但不能添加存储的属性,也不能向现有属性添加属性观察器。
初始化器
扩展可以将新的初始化程序添加到现有类型。这使您能够扩展其他类型,以接受您自己的自定义类型作为初始化程序参数,或提供未包含在该类型的原始实现中的其他初始化选项。
扩展可以向类添加新的便利初始化器,但不能向类添加新的指定的初始化器或反初始化器。指定的初始化程序和反初始化程序必须始终由原始类实现提供。
如果使用扩展将初始值设定项添加到为其所有存储属性提供默认值且未定义任何自定义初始值设定项的值类型,则可以从扩展程序的初始值设定项中调用该值类型的默认初始值设定项和成员明智的初始值设定项。如果您将初始化程序编写为值类型的原始实现的一部分,则情况并非如此,如“值类型的初始化程序委托”中所述。
如果使用扩展将初始化程序添加到在另一个模块中声明的结构中,则新的初始化程序将无法访问,self
除非它从定义模块中调用了初始化程序。
下面的示例定义了一个自定义Rect
结构来表示一个几何矩形。该示例还定义了两个称为Size
和的支持结构Point
,这两个结构都0.0
为其所有属性提供默认值:
- struct Size {
- var width = 0.0, height = 0.0
- }
- struct Point {
- var x = 0.0, y = 0.0
- }
- struct Rect {
- var origin = Point()
- var size = Size()
- }
因为该Rect
结构为其所有属性提供默认值,所以它会自动接收一个默认初始化程序和一个逐成员初始化程序,如Default Initializers中所述。这些初始化程序可用于创建新Rect
实例:
- let defaultRect = Rect()
- let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
- size: Size(width: 5.0, height: 5.0))
您可以扩展该Rect
结构,以提供一个附加的初始化程序,该初始化程序具有特定的中心点和大小:
- extension Rect {
- init(center: Point, size: Size) {
- let originX = center.x - (size.width / 2)
- let originY = center.y - (size.height / 2)
- self.init(origin: Point(x: originX, y: originY), size: size)
- }
- }
这个新的初始化程序通过基于提供的center
点和size
值计算适当的原点开始。然后,初始化器调用结构的自动成员式初始化器init(origin:size:)
,该初始化器将新的origin和size值存储在适当的属性中:
- let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
- size: Size(width: 3.0, height: 3.0))
- // centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
笔记
如果您为新的初始化程序提供了扩展名,则在初始化程序完成后,您仍然有责任确保每个实例都已完全初始化。
方法
扩展可以向现有类型添加新的实例方法和类型方法。下面的示例repetitions
向该Int
类型添加一个新的实例方法:
- extension Int {
- func repetitions(task: () -> Void) {
- for _ in 0..<self {
- task()
- }
- }
- }
该repetitions(task:)
方法接受一个类型为type的参数,该参数表示一个没有参数且不返回值的函数。() -> Void
定义此扩展名后,您可以repetitions(task:)
在任何整数上调用方法来执行多次任务:
- 3.repetitions {
- print("Hello!")
- }
- // Hello!
- // Hello!
- // Hello!
变异实例方法
添加了扩展名的实例方法还可以修改(或变异)实例本身。修改self
其属性的结构和枚举方法或其实例属性必须将实例方法标记为mutating
,就像从原始实现中对方法进行变异一样。
下面的示例square
为Swift的Int
类型添加了一个新的变异方法,该方法将原始值平方:
- extension Int {
- mutating func square() {
- self = self * self
- }
- }
- var someInt = 3
- someInt.square()
- // someInt is now 9
下标
扩展可以将新的下标添加到现有类型。本示例向Swift的内置Int
类型添加一个整数下标。此下标[n]
返回数字n
右边的小数位数:
123456789[0]
退货9
123456789[1]
退货8
…等等:
- extension Int {
- subscript(digitIndex: Int) -> Int {
- var decimalBase = 1
- for _ in 0..<digitIndex {
- decimalBase *= 10
- }
- return (self / decimalBase) % 10
- }
- }
- 746381295[0]
- // returns 5
- 746381295[1]
- // returns 9
- 746381295[2]
- // returns 2
- 746381295[8]
- // returns 7
如果该Int
值没有足够的位数来存储所请求的索引,则下标实现将返回0
,就好像该数字已在左侧填充了零:
- 746381295[9]
- // returns 0, as if you had requested:
- 0746381295[9]
嵌套类型
扩展可以将新的嵌套类型添加到现有的类,结构和枚举中:
- extension Int {
- enum Kind {
- case negative, zero, positive
- }
- var kind: Kind {
- switch self {
- case 0:
- return .zero
- case let x where x > 0:
- return .positive
- default:
- return .negative
- }
- }
- }
本示例向中添加了一个新的嵌套枚举Int
。此枚举称为Kind
,表示特定整数表示的数字的种类。具体来说,它表示数字是负数,零数还是正数。
此示例还向添加了一个新的计算实例属性Int
,称为kind
,该属性返回该Kind
整数的相应枚举大小写。
嵌套枚举现在可以与任何Int
值一起使用:
- func printIntegerKinds(_ numbers: [Int]) {
- for number in numbers {
- switch number.kind {
- case .negative:
- print("- ", terminator: "")
- case .zero:
- print("0 ", terminator: "")
- case .positive:
- print("+ ", terminator: "")
- }
- }
- print("")
- }
- printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
- // Prints "+ + - 0 - 0 + "
这个函数printIntegerKinds(_:)
接受一个输入Int
值数组,然后依次迭代这些值。对于数组中的每个整数,函数都会考虑该kind
整数的计算属性,并输出适当的描述。
笔记
number.kind
已知是类型的Int.Kind
。因此,所有Int.Kind
case值都可以用简写形式写在switch
语句中,例如.negative
而不是Int.Kind.negative
。