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

相关推荐
泉木4 小时前
Swift 从入门到精通-第四篇
swift
泉木4 小时前
Swift 从入门到精通-终篇
swift
ITKEY_8 小时前
appstore上架-预览和截屏
ios·appstore
阿捏利8 小时前
详解Mach-O(三十三)Mach-O __mod_term_func节
macos·ios·c/c++·mach-o
2501_9160074710 小时前
提高开发效率的尝试,用快蝎(kxapp)完成 iOS 项目的创建、调试与构建
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
2501_9151063210 小时前
如何在 Mac 上面代理抓包和数据流分析
android·macos·ios·小程序·uni-app·iphone·webview
blackorbird11 小时前
Coruna 间谍软件活动持续扩散,苹果破例为旧版iOS设备推送双版本安全补丁
macos·ios·objective-c·cocoa
for_ever_love__11 小时前
Objective-C 学习 单例模式
学习·ios·单例模式·objective-c
cola_wh11 小时前
避免团队多人 cocopods 冲突未验证
ios
2501_9159214311 小时前
在 Linux 上通过命令行上架 iOS APP,Fastlane + AppUploader(开心上架)
android·linux·运维·ios·小程序·uni-app·iphone