Swift科普文《associatedtype》
Swift科普文《associatedtype》

Swift科普文《associatedtype》

associatedtype 是 Swift 中用于协议的一种特性,它允许协议定义一个占位符类型,让遵循协议的类型在实现协议时决定这个占位符的实际类型。可以把 associatedtype 想象成协议中的一个 “类型变量”,类似于泛型。

为什么使用 associatedtype?

在协议中,通常会有一些操作或方法需要使用特定的类型,但协议本身并不知道那个类型是什么。例如,一个 Collection 协议应该能够表示一个元素序列,但这个协议不需要知道这些元素的具体类型。所以,我们就可以用 associatedtype 来表示这些元素。

举个例子:

假设我们要定义一个协议来描述可以绘制的对象,这个对象会有一个坐标类型,但我们不知道具体是用什么类型来表示坐标。所以,我们用 associatedtype 来定义一个占位符类型。

protocol Drawable {
    associatedtype Coordinate
    func draw(at point: Coordinate)
}

在这个例子中:

associatedtype Coordinate 声明了一个关联类型 Coordinate。这个 Coordinate 只是一个占位符,表示 “某种类型的坐标”,协议本身不关心具体是哪种类型。

遵循 Drawable 协议的类型将决定 Coordinate 的具体类型。

使用 associatedtype 的好处

1、灵活性:

遵循协议的类型可以自由决定 associatedtype 的实际类型。例如,一个类型可以用 CGPoint 表示坐标,而另一个类型可以用 Int 表示坐标。

2、通用性:

你可以编写通用的代码,而不需要关心具体的类型。例如,Collection 协议就是使用 associatedtype 来表示集合中的元素类型的。

实际使用: 假设我们要有两种遵循 Drawable 协议的类型:一个是用二维平面坐标的 Circle,另一个是用简单的整数坐标的 Pixel。

struct Circle: Drawable {
    // 决定实际的 Coordinate 类型
    typealias Coordinate = CGPoint
    
    func draw(at point: CGPoint) {
        print("Drawing circle at \(point)")
    }
}

struct Pixel: Drawable {
    // 决定实际的 Coordinate 类型
    typealias Coordinate = Int
    
    func draw(at point: Int) {
        print("Drawing pixel at position \(point)")
    }
}

在这个例子中:

Circle 类型使用 CGPoint 作为 Coordinate 类型,所以它的 draw(at:) 方法会接受 CGPoint。

Pixel 类型使用 Int 作为 Coordinate 类型,所以它的 draw(at:) 方法会接受 Int。

协议规定了 draw(at:) 方法的参数必须是 Coordinate 类型,但 Coordinate 是一个占位符,表示可以是任意类型。遵循协议的类型(如 Circle)需要告诉编译器 Coordinate 实际是哪种类型。

Circle 如何满足协议要求?

当 Circle 遵循 Drawable 协议时,使用了:

typealias Coordinate = CGPoint

表示 Circle 将 Coordinate 的实际类型设定为 CGPoint,这就满足了协议对 draw(at:) 方法的要求。因此,协议中的方法定义:

func draw(at point: Coordinate)

在 Circle 中被具体化为:

func draw(at point: CGPoint)

因为 Coordinate 已经被定义为 CGPoint,所以你在 Circle 的实现中使用 CGPoint 作为 draw(at:) 的参数类型。这并不是直接因为 typealias 而是因为这是协议要求的实现。

为什么使用 CGPoint 而不是 Coordinate?

你可以将实现写成这样:

func draw(at point: Coordinate) {
    print("Drawing circle at \(point)")
}

这种情况下,Coordinate 已经被设定为 CGPoint,所以效果是一样的。然而,大多数开发者在实现协议时更倾向于直接使用具体类型(这里是 CGPoint),因为这样代码的可读性会更高,让人更直观地知道参数的类型。

关联类型与泛型的关系:

associatedtype 类似于泛型,但它只能在协议中使用。而泛型可以在类、结构体、枚举以及函数中使用。两者的主要区别在于:

泛型: 在定义时指定实际类型,例如 func printArray<T>(array: [T])。

关联类型: 在协议中作为占位符,由遵循协议的类型在实现时决定实际类型。

总结

associatedtype 是用来定义协议中的类型占位符,让协议更通用,更灵活。遵循协议的类型可以指定具体的关联类型,这样你就可以编写能够处理各种类型的通用代码。

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注