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

相关推荐
missmisslulu10 小时前
电容笔值得买吗?2024精选盘点推荐五大惊艳平替电容笔!
学习·ios·电脑·平板
GEEKVIP11 小时前
手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]
android·macos·ios·智能手机·电脑·手机·iphone
GEEKVIP11 小时前
如何在 Windows 10 上恢复未保存/删除的 Word 文档
macos·ios·智能手机·电脑·word·笔记本电脑·iphone
奇客软件12 小时前
iPhone使用技巧:如何恢复变砖的 iPhone 或 iPad
数码相机·macos·ios·电脑·笔记本电脑·iphone·ipad
奇客软件2 天前
如何从相机的记忆棒(存储卡)中恢复丢失照片
深度学习·数码相机·ios·智能手机·电脑·笔记本电脑·iphone
GEEKVIP2 天前
如何修复变砖的手机并恢复丢失的数据
macos·ios·智能手机·word·手机·笔记本电脑·iphone
一丝晨光2 天前
继承、Lambda、Objective-C和Swift
开发语言·macos·ios·objective-c·swift·继承·lambda
GEEKVIP2 天前
iPhone/iPad技巧:如何解锁锁定的 iPhone 或 iPad
windows·macos·ios·智能手机·笔记本电脑·iphone·ipad
KWMax2 天前
RxSwift系列(二)操作符
ios·swift·rxswift