Swift 中的扩展函数、扩展静态函数和扩展 class 函数之间有什么区别?

What's the difference between an Extension func, Extension static func & Extension class func in Swift?

我试图在 UIColor 上创建一个扩展函数,它可以将 Card.Colour 类型的参数和 return UIColor 返回给调用者。

button.backgroundColor = UIColor.getColour(cardColour: cardToDeal.colour)


extension UIColor {
    func getColour(cardColour: Card.Colour) -> UIColor {
        switch cardColour {
        case .Red:
            return UIColor.red
        case .Green:
            return UIColor.green
        case .Blue:
            return UIColor.blue
        }
    }
}

当我尝试这样做时,UIColor.getColour的扩展函数要求我在扩展方法中输入 UIColor 类型的参数,而不是 Card.Colour 的指定类型。

但是,当我将 getColour 的扩展函数更改为:

static func getColour(cardColour: Card.Colour) -> UIColor {
class func getColour(cardColour: Card.Colour) -> UIColor {

它允许我传递 Card.Colour

类型的参数

这是为什么?为什么将函数更改为静态函数或 class 函数会更改需要传入的类型?

提前致谢!

(如能详细解答,将不胜感激)

扩展方法对提供类型的实例进行操作。您可以在方法块内使用实例的所有 internal 属性和方法。

static 方法是由 class 名称命名空间的方法,不对您的 class 的任何特定实例进行操作。 class 方法几乎相同,只是 classstatic 之间的区别在于,您可以在 subclass 中使用 override class 方法].

记住 UIColor 是 class。 class 有点像您用来创建符合 class 的实例或对象的蓝图。 UIColor.redUIColor class.

的一个实例

当您在 class 中定义 func 时(在您的情况下,作为扩展),Swift 假定您要将 func 添加到蓝图,它将依次在 UIColor class 的所有实例中可用,例如 UIColor.red.

您还可以在所有 class 之外定义您的 func,只需将它放在模块的顶层,而不是在 extension.[=52 中=]

但是,为了使您的函数井井有条,您可以将类似的函数放在 class 名称中。您只需告诉 Swift 您不是要将函数添加到将应用于所有 实例 的蓝图中,而是您想要的只是拥有一个函数,其名称以 class.

的名称为前缀

这里有一个例子来说明用法上的区别:

class Test {
    func notStatic() {
        print("called from an instance")
    }

    static func thisIsStatic() {
        print("called on class name directly")
    }
}

let instance = Test() // this is an *instance* of Test

instance.notStatic() // we can call a non static func on instance

Test.thisIsStatic() // we can call a static func directly on the class only

现在,让我们暂时回到您的具体示例。请注意,在您的示例中,您从 Card.Colour 的实例开始并尝试创建 UIColor.

new 实例

换句话说,将 func 添加到 UIColor 实例 (即非 staticclass ) 对你没用,因为你还没有 UIColor 的实例。

创建 class 新实例的惯用方法是使用初始化器 (init)。所以你可以像这样把你的函数变成 UIColor 的初始化器:

extension UIColor {
    convenience init(cardColour: Card.Colour) {
        switch cardColour {
        case .Red: self.init(cgColor: UIColor.red.cgColor)
        case .Blue: self.init(cgColor: UIColor.blue.cgColor)
        case .Green: self.init(cgColor: UIColor.green.cgColor)
        }
    }
}

现在您只需调用 UIColor(cardColour: .Red) 即可获得您想要的。请注意,在实现中,我将 UIColor.red 转换为 cgColor 并作为快速破解。对于 Card.Colour.

的每种情况,请随意使用您认为适合 UIColor 的初始化程序

但还有另一种方式,我认为这种方式更加优雅。由于您已经有一个 Card.Colour 实例 ,您可以使用一个函数扩展 Card.Colour,为您提供对应于该实例的 UIColor。在该函数中,您可以使用关键字 self.

引用 Card.Colour 实例

由于您已经拥有 Card.Colourself 的实例,因此您不需要向该函数传递任何参数。这允许您使用称为 计算属性 的很酷的功能,使使用更加方便。

这就是您将这样的扩展添加到 Card.Colour 的方式:

extension Card.Colour {
    var uiColor: UIColor {
        switch self {
        case .Red: return .red
        case .Blue: return .blue
        case .Green: return .green
        }
    }
}

然后你可以从 Card.Colour 得到一个 UIColor 像这样 Card.Colour.Red.uiColormainColour.uiColor,其中 mainColour 是类型 Card.Colour.

最后,正如 Leo Dabus 在评论中指出的那样,Swift's naming conventions 是大小写应以小写字母开头。您应该使用 Card.Colour.red 而不是 Card.Colour.Red,等等。这些约定出现了大约 Swift 3 次。在此之前将案例名称大写是很常见的。