swift-14-可选项的本质、运算符重载、扩展( Extension )
一、可选项的本质
1.1 可选项的本质是enum类型
public enum Optional<Wrapped> : ExpressibleByNilLiteral
case none
case some(Wrapped)
public init_ some: Wrapped)
}
1.2 平时写的可选项对应的原始写法
var age: Int? = 10
var age0: Optional<Int> = Optional<Int>.some(10)
var age1: Optional = .some(10)
var age2 = Optional.some(10)
var age3 = Optional(10)
age = nil
age3 = .nonevar age: Int? = nil
var age0 = Optional<Int>.none
var age1: Optional<Int> = .none可以混合写
var age: Int? = .none
age = 10
age = .some(20) age = nil
1.3 平时我们写的非枚举可选(本质就是枚举)和非可选类型支持switch 的写法
var age: int ? = 10
switch age {case let v?:
print("some", v)
case nil:
print("none")
}
上面的写法等价于
if let v = age
{
print("1", v)
}
else {
print ("2" )
}
var age: int = 10 switch age {
case let .some(v):
print("some", v)
case .none:
print("none")
}
1.4 多重可选项
var age_: Int? = 10
var age: Int?? = age_
age = nilvar age0 = Optional.some(Optional.some(10)) age0 = .none
var age1: Optional<Optional> = .some(.some(10)) age1 = .nonevar age: Int?? = 10
var age0: Optional<Optional> = 10
二、溢出运算符(Overflow Operator)
2.1 类型范围区间
print(Int8.min) // -128print(Int8.max) // 127print(UInt8.min) // 0
print(UInt8.max) // 255
2.2 那么溢出之后如何处理
Swift的算数运算符出现溢出时会抛出运行时错误
Swift有溢出运算符( &+、 &-、 &*),用来支持溢出运算
假如范围是-128到127 之间,只用溢出运算符( &+、 &-、 &*)后,数字依然会在-128到127这间进行变化,例如 当前数是a=127 使用了b=a&+ 1 ,那么b的值是-127,又回到最小的-127了
var min = UInt8.min
print(min &- 1) // 255, Int8.maxvar max = UInt8.max
print(max &+ 1) // 0, Int8.min
print(max &* 2) // 254, 等价于 max &+ max
三、运算符重载( Operator Overload )
类、结构体、枚举可以为现有的运算符提供自定义的实现,这个操作叫做:运算符重载
注:重载的运算符一般放在对应的类、结构体、枚举内,写成static 的形式
struct Point {
var x: Int, y: Int
}func + (p1: Point, p2: Point) -> Point {
Point(x: p1.x + p2.x, y: p1.y + p2.y)
}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)
}
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 协议,重载 == 运算符
1、与此同时,等价于重载了 != 运算符
struct Point : 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
注:有两种方式实现,第一种是遵守Equatable,第二种重载==运算符,但是一般情况下如果要比较2个实例是否等价,都需要遵守Equatable,因为如果我要定义一个比较方法带范型的参数的时候,范型参数需要制定具体的遵守的类型
4.2 Swift为以下类型提供默认的 Equatable 实现
1、没有关联类型的枚举
enum Answer {
case wrong case right
}
var s1 = Ayswer. wrong
var s2 = Answer.right
print (s1 == s2)
2、只拥有遵守 Equatable 协议关联类型的枚举(枚举遵守 Equatable不用实现Equatable定义的方法,但其他的必须实现Equatable定义的方法才行)
3、只拥有遵守 Equatable 协议存储属性的结构体
一下Point : Equatable 提供了默认的实现,Point遵守Equatable也不用再去实现Equatable定义的方法
struct Point : Equatable {
var x = 0, y = 0
}
4、如果是类的话,必须自己实现Equatable定义的额方法
5、引用类型比较存储的地址值是否相等(是否引用着同一个对象),使用恒等运算符 === 、 !==
五、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代表在可选链操作中拥有跟赋值运算符一样的优先级 }prefix operator +++
infix operator +- : PlusMinusPrecedence
precedencegroup PlusMinusPrecedence {
associativity: none
higherThan: AdditionPrecedence
lowerThan: MultiplicationPrecedence assignment: true
}
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)
扩展可以为枚举、结构体、类、协议添加新功能
可以添加方法、计算属性、下标、(便捷)初始化器、嵌套类型、协议等等
扩展不能办到的事情
不能覆盖原有的功能
不能添加存储属性,不能向已有的属性添加属性观察器
不能添加父类
不能添加指定初始化器,不能添加反初始化器 ...
7.1 计算属性、下标、方法、嵌套类型
extension Double {
var km: Double { self * 1_000.0 }
var m: Double { self }
var dm: Double { self / 10.0 }
var cm: Double { self / 100.0 }
var mm: Double { self / 1_000.0 }
}var b = 100.0b.km
为数组扩展,防止越界
extension Array {
subscript(nullable idx: Int) -> Element? {
if (startIndex..<endIndex).contains(idx) {
return self [idx]
}
return 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
}
}
7.2 协议、初始化器
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)
required初始化器也不能写在扩展中
八、协议
如果一个类型已经实现了协议的所有要求,但是还没有声明它遵守了这个协议
可以通过扩展来让它遵守这个协议
protocol TestProtocol {
func test()
}
class TestClass {
func test() {
print("test")
}
}
extension TestClass : TestProtocol {}
编写一个函数,判断一个整数是否为奇数?
谁都可以调用
func isOdd<T: BinaryInteger>(_ i: T) -> Bool { i % 2 != 0
}只为 BinaryInteger 进行扩展isOdd
extension BinaryInteger {
func isOdd() -> Bool { self % 2 != 0 }
}
扩展可以给协议提供默认实现,也间接实现『可选协议』的效果
扩展可以给协议扩充『协议中从未声明过的方法』
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 test1cls.test2() // 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
var cls2: TestProtocol = TestClass()
TestProtocol 这里面没有声明test2,所以他不能确定将来指向的实列对象有没有有没有test2的实现
var cls2: TestProtocol = TestClass()
cls2.test2() 这个就当实力里面可能不存在的,就当他不存在,就不从实列里面去找,先从TestProtocol里面去优先找,于是就是下边的这种情况
cls2.test1() test1协议里面定义了test1,他指向的实列对象必须实现test1,因此这个就先从TestClass()里面找
var cls2: TestProtocol = TestClass()
cls2.test1() // TestClass 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! }
}
// 符合条件才扩展
extension Stack : Equatable where E : Equatable {
static func == (left: Stack, right: Stack) -> Bool {
left.elements == right.elements
}
}