高级 swift 教程:自定义运算符和自定优先级

关注我,每天分享一个关于 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:运算符的结合性,可以是 leftrightnone。如果运算符的优先级相同,那么结合性将决定运算的顺序。

下面是一个例子🌰:

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新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

相关推荐
大熊猫侯佩1 小时前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(三)
数据库·swiftui·swift
大熊猫侯佩1 小时前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(二)
数据库·swiftui·swift
大熊猫侯佩1 小时前
用异步序列优雅的监听 SwiftData 2.0 中历史追踪记录(History Trace)的变化
数据库·swiftui·swift
大熊猫侯佩1 小时前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(一)
数据库·swiftui·swift
Jouzzy5 小时前
【iOS安全】iPhone X iOS 16.7.11 (20H360) WinRa1n 越狱教程
安全·ios·iphone
二流小码农17 小时前
鸿蒙开发:实现一个标题栏吸顶
android·ios·harmonyos
season_zhu18 小时前
iOS开发:关于日志框架
ios·架构·swift
程序员老刘18 小时前
20%的选择决定80%的成败
flutter·架构·客户端
Digitally21 小时前
如何在电脑上轻松访问 iPhone 文件
ios·电脑·iphone
安和昂21 小时前
【iOS】YYModel源码解析
ios