扩展

扩展

为现有类型添加功能。

扩展(Extensions) 用于为现有的类、结构体、枚举或协议类型添加新功能。这包括了扩展那些您无法访问原始源代码的类型的能力(即追溯建模)。扩展和 Objective-C 的分类很相似。(与 Objective-C 分类不同的是,Swift 扩展是没有名字的。)

Swift 中的扩展可以:

- 添加计算实例属性和计算类属性 - 定义实例方法和类方法 - 提供新的构造器 - 定义下标 - 定义和使用新的嵌套类型 - 使已经存在的类型遵循一个协议

在 Swift 中,你甚至可以扩展协议以提供其需要的实现,或者添加额外功能给遵循的类型所使用。你可以从 获取更多细节。

扩展的语法

使用 extension 关键字声明扩展:

extension SomeType {
    // 在这里给 SomeType 添加新的功能
}

扩展可以扩充一个现有的类型,给它添加一个或多个协议。在添加协议的遵循声明时,协议名称的写法和类或者结构体一样:

extension SomeType: SomeProtocol, AnotherProtocol {
    // 协议所需要的实现写在这里
}

这种遵循协议的方式在 中有描述。

扩展可以使用在现有泛型类型上,就像 中描述的一样。你还可以使用扩展给泛型类型有条件地添加功能,就像 中描述的一样。

计算属性

扩展可以给现有类型添加计算实例属性和计算类属性。这个例子给 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")
// 打印“One inch is 0.0254 meters”
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// 打印“Three feet is 0.914399970739201 meters”

这些计算属性表示一个 Double 值应该被视为某种长度单位。尽管它们是作为计算属性实现的,但是这些属性的名称可以使用点语法附加到浮点字面量值之后,作为一种使用该字面量值执行距离转换的方式。

在这个例子中,Double 类型的 1.0 代表的是“一米”。这就是为什么计算属性 m 返回的是 self ——表达式 1.m 被认为是计算一个 Double 类型的 1.0

其他单位需要进行一些转换,才能表示为以米为单位的值。一千米等于 1000 米,所以计算属性 km 将该值乘以 1_000.00来将其转换为以米为单位的数字。类似地,一米等于 3.28084 英尺,因此计算属性 ft 将底层的 Double 值除以 3.28084 来将其从英尺转换为米。

这些属性是只读的计算属性,因此为了简便,它们的表达方式省略了 get 关键字。它们的返回值是 Double 类型,可以在任何接受 Double 的数学计算中使用:

let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
// 打印“A marathon is 42195.0 meters long”

构造器

扩展可以为现有类型添加新的构造器。这使你可以扩展其他类型以接受你自己的自定义类型作为构造器参数,或提供类型的原始实现中未包含的其他构造选项。

扩展可以为一个类添加新的便利构造器(convenience initializer),但是它们不能为一个类添加新的指定构造器(designated initializer)或析构器(deinitializer)。指定构造器和析构器必须始终由类的原始实现提供。

如果你使用扩展为一个值类型添加构造器,并且该值类型提供了所有存储属性的默认值,且没有定义任何自定义构造器,你就可以在扩展的构造器中调用该值类型的默认构造器和成员构造器。如果你已经将构造器写在该值类型的原始实现中,则不适用于这种情况,正如 中所描述的那样。

如果你使用扩展为另一个模块中声明的结构体添加构造器,那么在调用定义模块中的构造器之前,新的构造器是不能访问 self 的。

以下示例定义了一个自定义的 Rect 结构体来表示几何矩形。该示例还定义了两个辅助结构体 SizePoint,它们都为所有属性提供了默认值 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 结构体为所有属性都提供了默认值,它会自动获得默认构造器和成员构造器,如 中所述。这些构造器可用于创建新的 Rect 实例:

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
    size: Size(width: 5.0, height: 5.0))

你可以通过扩展 Rect 结构体来额外提供一个允许指定 center 和 size 的构造器:

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)
    }
}

这个新的构造器首先根据提供的 centersize 计算一个适当的原点。然后这个构造器调用结构体自带的成员构造器 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 的 origin 是 (2.5, 2.5) 并且它的 size 是 (3.0, 3.0)

方法

扩展可以为现有类型添加新的实例方法和类方法。以下示例为 Int 类型添加了一个名为 repetitions 的新实例方法:

extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0..<self {
            task()
        }
    }
}

repetitions(task:) 方法仅接受一个类型为 () -> Void 的参数,它表示一个没有参数没有返回值的函数。

定义了这个扩展之后,你可以对任意整形数值调用 repetitions(task:) 方法,以执行指定次数的任务:

3.repetitions {
    print("Hello!")
}
// Hello!
// Hello!
// Hello!

变值实例方法

通过扩展添加的实例方法同样也可以修改(modify)(或 改变(mutating))实例本身。修改 self 或其属性的结构体和枚举方法,必须将实例方法标记为 mutating,就像原始实现中的变值方法(mutating methods)一样。

下面的示例为 Swift 的 Int 类型添加了一个新的变值方法 square,它可以计算原始值的平方:

extension Int {
    mutating func square() {
        self = self * self
    }
}
var someInt = 3
someInt.square()
// someInt 现在是 9

下标

扩展可以为现有类型添加新的下标。这个示例为 Swift 内置的 Int 类型添加了一个整数下标。下标 [n] 返回数字从右边数第 n 位的十进制数字:

……以此类推:

extension Int {
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..<digitIndex {
            decimalBase *= 10
        }
        return (self / decimalBase) % 10
    }
}
746381295[0]
// 返回 5
746381295[1]
// 返回 9
746381295[2]
// 返回 2
746381295[8]
// 返回 7

如果 Int 值的数字位数不足以满足所请求的索引,那么下标实现会返回 0,就好像在数字左边补上了 0:

746381295[9]
// 返回 0,就好像你进行了这个请求:
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])
// 打印“+ + - 0 - 0 + ”

这个函数 printIntegerKinds(_:) 接受一个 Int 值的数组作为输入,并逐个遍历这些值。对于数组中的每个整数,该函数都会检查它的 kind 计算属性,然后打印适当的描述。

← 返回目录