Swift第八章 高级运算符、扩展
高级运算符
溢出运算符(Overflow Operator)
- Swift的算数运算符出现溢出时会抛出运行时错误
-
Swift有溢出运算符(&+、&-、&*),用来支持溢出运算
var min = UInt8.min //无符号整形最小值,0 //溢出报错 //min -= 1 //使用溢出运算符 print(min &- 1) // 255, Int8.max var max = UInt8.max //无符号整形最大值,255 //溢出报错 //min += 1 //使用溢出运算符 print(max &+ 1) // 0, Int8.min print(max &* 2) // 254, 等价于 max &+ max
运算符重载(Operator Overload)
-
类、结构体、枚举可以为现有的运算符提供自定义的实现,这个操作叫做:运算符重载
struct Point { var x: Int, y: Int } //运算符重载 func + (p1: Point, p2: Point) -> Point { Point(x: p1.x + p2.x, y: p1.y + p2.y) } //实现2个结构体相加 let p = Point(x: 10, y: 20) + Point(x: 11, y: 22) print(p) // Point(x: 21, y: 42) //也可以将重载运算符放到结构体里面 struct Point { var x: Int, y: Int static func + (p1: Point, p2: Point) -> Point { Point(x: p1.x + p2.x, y: p1.y + p2.y) } } -
举例
static func + (p1: Point, p2: Point) -> Point { Point(x: p1.x + p2.x, y: p1.y + p2.y) } static func - (p1: Point, p2: Point) -> Point { Point(x: p1.x - p2.x, y: p1.y - p2.y) } //前缀运算符重载 static prefix func - (p: Point) -> Point { Point(x: -p.x, y: -p.y) } //修改外部的参数,因此使用inout static func += (p1: inout Point, p2: Point) { p1 = p1 + p2 } //前缀运算符重载 static prefix func ++ (p: inout Point) -> Point { p += Point(x: 1, y: 1) return p } //后缀运算符重载 static postfix func ++ (p: inout Point) -> Point { let tmp = p p += Point(x: 1, y: 1) return tmp } static func == (p1: Point, p2: Point) -> Bool { (p1.x == p2.x) && (p1.y == p2.y) }
Equatable
- 要想得知2个实例是否等价,一般做法是遵守 Equatable 协议,重载 == 运算符
- 与此同时,等价于重载了 != 运算符
- Swift为以下类型提供默认的 Equatable 实现,其他类型需要主动实现,比如类
- 没有关联类型的枚举
- 只拥有遵守 Equatable 协议关联类型的枚举,即:有关联类型,但是关联的那个类型也遵守了Equatable
- 只拥有遵守 Equatable 协议存储属性的结构体,即:结构体的存储属性,都遵守了Equatable
//有关联类型,但是关联的那个类型也遵守了Equatable enum Answer : Equatable { //Int、String本质都遵守了Equatable case wrong(Int, String) case right } struct Point : Equatable { //两个存储属性都遵守了Equatable var x: Int, y: Int } var p1 = Point(x: 10, y: 20) var p2 = Point(x: 11, y: 22) print(p1 == p2) // false print(p1 != p2) // true //类需要自己实现 class Person : Equatable { var age : Int init (age: Int) { self.age = age } //自己实现==重载 static func == (lhs : Person , rhs: Person) -> Bool { lhs.age == rhs.age } } - 引用类型比较存储的地址值是否相等(是否引用着同一个对象),使用恒等运算符 === 、!==,例:比较两个对象的地址值是否相等。
Comparable
- 要想比较2个实例的大小,一般做法是:遵守 Comparable 协议、重载相应的运算符
// score大的比较大,若score相等,age小的比较大
struct Student : Comparable {
var age: Int
var score: Int
init(score: Int, age: Int) {
self.score = score
self.age = age
}
static func < (lhs: Student, rhs: Student) -> Bool {
(lhs.score < rhs.score)
|| (lhs.score == rhs.score && lhs.age > rhs.age)
}
static func > (lhs: Student, rhs: Student) -> Bool {
(lhs.score > rhs.score)
|| (lhs.score == rhs.score && lhs.age < rhs.age)
}
static func <= (lhs: Student, rhs: Student) -> Bool {
!(lhs > rhs)
}
static func >= (lhs: Student, rhs: Student) -> Bool {
!(lhs < rhs)
}
}
var stu1 = Student(score: 100, age: 20)
var stu2 = Student(score: 98, age: 18)
var stu3 = Student(score: 100, age: 20)
print(stu1 > stu2) // true
print(stu1 >= stu2) // true
print(stu1 >= stu3) // true
print(stu1 <= stu3) // true
print(stu2 < stu1) // true
print(stu2 <= stu1) // true
自定义运算符(Custom Operator)
- 可以自定义新的运算符:在全局作用域使用operator进行声明
-
定义方式
prefix operator 前缀运算符 postfix operator 后缀运算符 infix operator 中缀运算符 : 优先级组 precedencegroup 优先级组 { associativity: 结合性(left\right\none) higherThan: 比谁的优先级高 lowerThan: 比谁的优先级低 assignment: true代表在可选链操作中拥有跟赋值运算符一样的优先级 } - Apple文档参考:
- https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations
- https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID380
-
案例:
//自定义3个运算符 prefix operator +++ infix operator +- : PlusMinusPrecedence //定义优先级组 precedencegroup PlusMinusPrecedence { associativity: none higherThan: AdditionPrecedence lowerThan: MultiplicationPrecedence assignment: true } //重载3个运算符 struct Point { var x: Int, y: Int static prefix func +++ (point: inout Point) -> Point { point = Point(x: point.x + point.x, y: point.y + point.y) return point } static func +- (left: Point, right: Point) -> Point { return Point(x: left.x + right.x, y: left.y - right.y) } static func +- (left: Point?, right: Point) -> Point { print("+-") return Point(x: left?.x ?? 0 + right.x, y: left?.y ?? 0 - right.y) } } //使用自定义运算符 struct Person { var point: Point } var person: Person? = nil person?.point +- Point(x: 10, y: 20)
扩展(Extension)
- Swift中的扩展,有点类似于OC中的分类(Category)
- 扩展可以为枚举、结构体、类、协议添加新功能
- 可以添加方法、计算属性、下标、(便捷)初始化器、嵌套类型、协议等等
- 扩展不能办到的事情
- 不能覆盖原有的功能
- 不能添加存储属性,不能向已有的属性添加属性观察器
- 不能添加父类
- 不能添加指定初始化器,不能添加反初始化器
计算属性、下标、方法、嵌套类型
```
//为Double新增只读计算属性
extension Double {
var km: Double { self * 1_000.0 } //get方法的缩写
var m: Double { self }
var dm: Double { self / 10.0 }
var cm: Double { self / 100.0 }
var mm: Double { self / 1_000.0 }
}
var d =100.0
print(d.km)
//为Array扩展一个下标,防止数组越界运行时报错,闪退
extension Array {
subscript(nullable idx: Int) -> Element? {
//startIndex数组开始索引、endIndex数组结束索引+1
if (startIndex..<endIndex).contains(idx) {
return self[idx]
}
return nil
}
}
//使用
var arr:Array<Int> = [10,20,30]
print(arr[nullable:10] as Any) //返回值为nil,不会闪退
//为Int扩展一些方法
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self { task() }
}
//求平方
mutating func square() -> Int {
self = self * self
return self
}
// 内部定义嵌套类型
enum Kind { case negative, zero, positive }
var kind: Kind {
switch self {
case 0: return .zero
case let x where x > 0: return .positive
default: return .negative
}
}
//下标
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..<digitIndex { decimalBase *= 10 }
return (self / decimalBase) % 10
}
}
//举例使用
2.repetitions{
print(1)
}
```
协议、初始化器
- 如果希望自定义初始化器的同时,编译器也能够生成默认初始化器,可以在扩展中编写自定义初始化器
-
required初始化器不能写在扩展中
class Person { var age: Int var name: String init(age: Int, name: String) { self.age = age self.name = name } } //为类扩展一个协议 extension Person : Equatable { static func == (left: Person, right: Person) -> Bool { left.age == right.age && left.name == right.name } convenience init() { self.init(age: 0, name: "") } } struct Point { var x: Int = 0 var y: Int = 0 } //扩展初始化器,尽管这里自定义了初始化器,编译器还是会自动生成默认初始化器 extension Point { init(_ point: Point) { self.init(x: point.x, y: point.y) } } //使用默认初始化器初始化 var p1 = Point() var p2 = Point(x: 10) var p3 = Point(y: 20) var p4 = Point(x: 10, y: 20) //使用自定义初始化器初始化 var p5 = Point(p4) -
如果一个类型已经实现了协议的所有要求,但是还没有声明它遵守了这个协议,可以通过扩展来让它遵守这个协议
protocol TestProtocol { func test() } //这个类没有遵守协议,但是他实现了协议 class TestClass { func test() { print("test") } } //可以用扩展来遵守这个协议 extension TestClass : TestProtocol {} //编写一个函数,判断一个整数是否为奇数? //所有的整数都默认遵守了BinaryInteger协议,直接扩展这个协议即可 extension BinaryInteger { func isOdd() -> Bool { self % 2 != 0 } } print(4.isOdd()) - 扩展可以给协议提供默认实现,也间接实现了『可选协议』的效果
-
扩展可以给协议扩充『协议中从未声明过的方法』
protocol TestProtocol { func test1() } extension TestProtocol { //原有协议扩展默认实现 func test1() { print("TestProtocol test1") } func test2() { print("TestProtocol test2") } } //扩展中有实现,所以这个类不用实现 class TestClass : TestProtocol {} var cls = TestClass() cls.test1() // TestProtocol test1 cls.test2() // TestProtocol test2 var cls2: TestProtocol = TestClass() cls2.test1() // TestProtocol test1 cls2.test2() // TestProtocol test2 class TestClass : TestProtocol { func test1() { print("TestClass test1") } func test2() { print("TestClass test2") } } var cls = TestClass() cls.test1() // TestClass test1 cls.test2() // TestClass test2 var cls2: TestProtocol = TestClass() cls2.test1() // TestClass test1 cls2.test2() // TestProtocol test2
泛型
-
可以对泛型类型做扩展
class Stack<E> { var elements = [E]() func push(_ element: E) { elements.append(element) } func pop() -> E { elements.removeLast() } func size() -> Int { elements.count } } // 扩展中依然可以使用原类型中的泛型类型 extension Stack { //给原来的泛型类型扩展一个实例方法 func top() -> E { elements.last! } } // 符合条件才扩展,当泛型里面的类型E遵守Equatable协议才给这个泛型类型扩展 extension Stack : Equatable where E : Equatable { static func == (left: Stack, right: Stack) -> Bool { left.elements == right.elements } }