返回

Protocol揭秘——理解实际应用中的类型擦除、不透明类型和泛型

IOS

在Swift编程中,协议(Protocol)是一种非常强大的工具,它允许我们定义一组方法和属性,从而实现代码的复用和抽象。然而,当你深入研究这些协议的实际应用时,可能会遇到一些复杂的问题,比如类型擦除、不透明类型和泛型的使用。本文将通过一个实际的编译错误案例,深入剖析这些概念,并提供解决方案。

什么是协议?

协议在Swift中是一种非常强大的工具,它允许我们定义一组方法和属性,而无需指定这些方法和属性的具体实现。协议可以被类、结构体和枚举所采用,从而实现代码的复用和抽象。

类型擦除

类型擦除的概念

类型擦除是Swift中一个非常重要的概念。当我们定义一个协议时,实际上是在定义一个类型。但是,这个类型并没有具体的行为,而只规定了一些方法和属性。当我们采用一个协议时,编译器会将这个协议擦除,只保留它的方法和属性。也就是说,在运行时,我们无法知道采用这个协议的具体类型是什么。

类型擦除的实际应用

假设我们有一个协议 Shape,它定义了 area()perimeter() 两个方法。

protocol Shape {
    func area() -> Double
    func perimeter() -> Double
}

现在,我们有两个类 CircleSquare,它们都采用了 Shape 协议。

class Circle: Shape {
    let radius: Double

    init(radius: Double) {
        self.radius = radius
    }

    func area() -> Double {
        return .pi * radius * radius
    }

    func perimeter() -> Double {
        return 2 * .pi * radius
    }
}

class Square: Shape {
    let sideLength: Double

    init(sideLength: Double) {
        self.sideLength = sideLength
    }

    func area() -> Double {
        return sideLength * sideLength
    }

    func perimeter() -> Double {
        return 4 * sideLength
    }
}

现在,我们想要写一个函数来计算一组形状的总面积。

func totalArea(shapes: [Shape]) -> Double {
    var totalArea: Double = 0

    for shape in shapes {
        totalArea += shape.area()
    }

    return totalArea
}

这个函数可以接受任何类型的形状数组,并计算它们的总面积。这是因为 Shape 协议是一个不透明类型,编译器不知道它会被哪些类型采用。因此,函数可以接受任意类型的形状。

编译错误

现在,让我们来修改一下 totalArea() 函数,看看会发生什么。

func totalArea(shapes: [Shape]) -> Int {
    var totalArea: Int = 0

    for shape in shapes {
        totalArea += shape.area()
    }

    return totalArea
}

编译器会报出一个错误:“二进制运算符 '+' 无法应用于类型 'Int' 和 'Double'”。这是因为 shape.area() 方法返回一个 Double 值,而 totalArea 变量是一个 Int 值。编译器无法将 Double 值直接加到 Int 值上。

这个错误告诉我们,不能将不同类型的形状直接加到一起。我们需要一种方法来将不同类型的形状转换为相同的类型。这就是泛型发挥作用的地方。

使用泛型

我们可以使用泛型来重写 totalArea() 函数,使其能够接受任何类型的形状数组,并返回任何类型的总面积。

func totalArea<T: Shape>(shapes: [T]) -> Double {
    var totalArea: Double = 0

    for shape in shapes {
        totalArea += shape.area()
    }

    return totalArea
}

这个函数现在可以使用任何类型的形状数组,并返回一个 Double 值。这是因为泛型 T 允许函数接受任何类型的形状,而 Double 值是所有数值类型的一个共同祖先。

总结

在本文中,我们探讨了 Swift 协议背后的故事,并学习了类型擦除、不透明类型和泛型的概念。我们还通过一个实际的例子看到了泛型是如何工作的。希望这些知识能够帮助你更好地理解 Swift 协议,并在你的项目中使用它们。

如果你对协议有更多的疑问或想深入了解更多关于 Swift 的高级特性,请参考以下资源: