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

相关推荐
白玉cfc10 小时前
【iOS】push,pop和present,dismiss
macos·ios·cocoa
低调小一11 小时前
Swift 语法学习指南 - 与 Kotlin 对比
微信·kotlin·swift
HarderCoder11 小时前
Swift Package Plugin 深度实战:从原理到落地,自动生成字体枚举
swift
低调小一11 小时前
iOS 开发入门指南-HelloWorld
ios
2501_9159184111 小时前
iOS 开发全流程实战 基于 uni-app 的 iOS 应用开发、打包、测试与上架流程详解
android·ios·小程序·https·uni-app·iphone·webview
用户8705681304517 小时前
iOS 异步渲染:从 CALayer 切入的实现与优化
ios
东坡肘子18 小时前
从开放平台到受控生态:谷歌宣布 Android 开发者验证政策 | 肘子的 Swift 周报 #0101
android·swiftui·swift
HarderCoder19 小时前
用 `defer` 管理异步清理:Swift 中的“保险丝”模式
swift
大熊猫侯佩1 天前
冰火岛 Tech 传:Apple Foundation Models 心法解密(上集)
llm·ai编程·swift
HarderCoder1 天前
深入理解 SwiftUI 的 Structural Identity:为什么“换个条件分支”就会丢状态?
swiftui·swift