关注我,每天分享一个关于 iOS 的新知识
前言
Swift 中的自定义运算符可以使代码更易于阅读和维护。一定程度上也可以让你编写更少的代码,同时保持逻辑清晰。
在 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)
}
}
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
中的 <- 运算符:
swift
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: ()
}
}
还有 >>> 运算符:
swift
infix operator >>>
public func >>> <T>(left: T, right: Map) {
if right.mappingType == .toJSON {
ToJSON.basicType(left, map: right)
}
}
运算符的三个类型
自定义运算符又分为三种类型,即:前缀、中缀和后缀。
1. 前缀运算符(Prefix Operator)
顾名思义,这种运算符位于操作数之前。用 prefix operator
来声明,如下面的例子中,定义了一个前缀运算符 √
,用于取一个浮点数的平方根。
swift
prefix operator √
prefix func √ (number: Double) -> Double {
return sqrt(number)
}
let number = 4.0
print(√number) // 输出结果为2.0
2. 中缀运算符(Infix Operator)
这种运算符位于两个操作数之间,这个也是平时最常用的。用 infix operator
来声明,如下面的例子中,定义了一个中缀运算符 **
,用于进行乘方运算。
swift
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。
swift
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
。如果运算符的优先级相同,那么结合性将决定运算的顺序。
下面是一个例子🌰:
swift
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]
苹果官方网站: developer.apple.com/documentati...
这里每天分享一个 iOS 的新知识,快来关注我吧
本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!