• Swift中自定义运算符的用法
  • 发布于 2个月前
  • 189 热度
    0 评论
前言
Swift 中的自定义运算符可以使代码更易于阅读和维护。一定程度上也可以让你编写更少的代码,同时保持逻辑清晰。在 Swift 中开发代码时,我们都经常使用默认运算符,比如使用 + 符号将两个数字相加,或者将两个字符串相加。今天来聊聊自定义运算符。

重载运算符
运算符重载允许在自定义类或结构体上使用已有的运算符(关于已有的运算符可以看文章底部的官网链接)。这是通过定义类型方法来实现的,这个类型方法的名称就是需要重载的运算符。

举个例子:
struct Vector2D {
    var x = 0.0, y = 0.0
}

extension Vector2D {
    static func + (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
}
// 堆代码 duidaima.com
let vector1 = Vector2D(x: 1.0, y: 2.0)
let vector2 = Vector2D(x: 3.0, y: 4.0)
let vectorSum = vector1 + vector2
print(vectorSum) // 输出结果为Vector2D(x: 4.0, y: 6.0)
这个示例中,我们重载了 "+" 运算符,让它可以用于 Vector2D 结构体。现在, "+" 运算符可以接受两个 Vector2D 实例作为参数,并返回一个新的 Vector2D 实例,这个实例的 x 和 y 值是参数的 x 和 y 值的和。

自定义运算符
除了重载运算符,还可以创建自己的自定义运算符。自定义运算符需要使用 operator 关键字进行声明,并需要指定它是一个前缀、中缀或后缀运算符。
这在很多开源项目中都有用到,比如 ObjectMapper 中的 <- 运算符:
infix operator <-
public func <- <T>(left: inout T, right: Map) {
 switch right.mappingType {
 case .fromJSON where right.isKeyPresent:
  FromJSON.basicType(&left, object: right.value())
 case .toJSON:
  left >>> right
 default: ()
 }
}
还有 >>> 运算符:
infix operator >>>
public func >>> <T>(left: T, right: Map) {
 if right.mappingType == .toJSON {
  ToJSON.basicType(left, map: right)
 }
}

运算符的三个类型
自定义运算符又分为三种类型,即:前缀、中缀和后缀。
1. 前缀运算符(Prefix Operator)
顾名思义,这种运算符位于操作数之前。用 prefix operator 来声明,如下面的例子中,定义了一个前缀运算符 √,用于取一个浮点数的平方根。
prefix operator √
prefix func √ (number: Double) -> Double {
    return sqrt(number)
}
let number = 4.0
print(√number) // 输出结果为2.0
2. 中缀运算符(Infix Operator
这种运算符位于两个操作数之间,这个也是平时最常用的。用 infix operator 来声明,如下面的例子中,定义了一个中缀运算符 **,用于进行乘方运算。
infix operator **: MultiplicationPrecedence
func ** (num: Double, power: Double) -> Double {
    return pow(num, power)
}
let result = 2.0 ** 3.0
print(result) // 输出结果为8.0
3. 后缀运算符(Postfix Operator)
这种运算符位于操作数之后。如下面的例子中,用 postfix operator 来声明,定义了一个后缀运算符 --,用于将一个整数减1。
postfix operator --
postfix func -- (num: inout Int) -> Int {
    num -= 1
    return num
}
var number = 5
print(number--) // 输出结果为4

优先级
学会了自定义运算符后,你可能有个疑问,如果一个表达式中有多个运算符,应该先计算哪个后计算哪个?比如  1 + 2 * 3,在这个表达式中,乘法运算符 * 的优先级高于加法运算符 +,因此先进行乘法运算,再进行加法运算,最终结果是 7,而不是 9。

swift 中使用优先级组(precedencegroup)的概念来确认谁的优先级更高,根据 swift 内置的优先级组,优先级是这样排列的:
BitwiseShiftPrecedence(位运算符组)> MultiplicationPrecedence(乘法运算符组)> AdditionPrecedence(加法运算符组)> RangeFormationPrecedence(区间运算符组)> CastingPrecedence > NilCoalescingPrecedence > ComparisonPrecedence > LogicalConjunctionPrecedence > LogicalDisjunctionPrecedence > TernaryPrecedence > AssignmentPrecedence

关于这些运算符组中都包含哪些运算符可以到苹果官方网站[1]中查看。

如果你自定义了一个新的运算符,并且想手动修改它的优先级,可以使用 precedencegroup 关键字来定义一个新的优先级组,然后在定义运算符时指定这个优先级组。优先级组可以设置以下几个属性:
higherThan:新的优先级组的优先级高于指定的优先级组。
lowerThan:新的优先级组的优先级低于指定的优先级组。
associativity:运算符的结合性,可以是 left、right 或 none。如果运算符的优先级相同,那么结合性将决定运算的顺序。
下面是一个例子:
precedencegroup ExponentiationPrecedence {
    higherThan: MultiplicationPrecedence
    associativity: right
}

infix operator **: ExponentiationPrecedence
func ** (num: Double, power: Double) -> Double {
    return pow(num, power)
}

let result = 2.0 * 3.0 ** 2.0
print(result) // 输出结果为18.0

这个示例中定义了一个新的优先级组 ExponentiationPrecedence,它的优先级高于乘法运算符的优先级组 MultiplicationPrecedence。然后定义了一个新的中缀运算符 **,并指定它的优先级组为 ExponentiationPrecedence。在计算 2.0 * 3.0 ** 2.0 这个表达式时,由于 ** 运算符的优先级高于 * 运算符,所以先计算 3.0 ** 2.0,得到结果 9.0,然后再与 2.0 相乘,得到结果 18.0。


参考资料
[1]苹果官方网站: https://developer.apple.com/documentation/swift/operator-declarations

用户评论