十五、可选项的本质、高级运算符(重载)、扩展

可选项的本质

可选项的本质是enum类型
Wrapped为泛型
var age: Int? = 10
age = 20
age = nil

//结合枚举及泛型理解
var age1: Optional<Int> = .some(10)
age1 = .some(20)
age = .none
var age: Int? = 10

var age2: Optional<Int> = Optional<Int>.some(10)//完整写法
var age3: Optional = .some(10)//通过10将Optional泛型类型确定
var age4 = Optional.some(10)//枚举变量赋值
var age5 = Optional(10)//枚举初始化器

age = nil//语法糖
age2 = .none//显式
var age6 = Optional<Int>.none//确定泛型类型 所以Int必须写
var age7 :Optional<Int> = .none

print(age == age2)//输出:true 就是一样的 仅写法不同

因此也可以通过Switch和if语句进行判断

switch age {
case let age? :
    print(age)
case nil :
    print("nil")
}

switch age {
case let .some(v) :// .some(let v) 都可以
    print(v)
case nil :
    print("nil")
}
多重可选类型
var age: Int? = 10
var age1: Int?? = age
age1 = nil

var age2 = Optional.some(Optional.some(10))
age2 = .none
var age3: Optional<Optional<Int>> = .some(.some(10))
var age4: Optional<Optional> = .some(.some(10))

var age5: Int?? = 10
var age6: Optional<Optional> = 10

高级运算符

溢出运算符
  • Swift的算术运算符出现溢出时会抛出运行时错误
  • Swift有溢出运算符(&+、&-、&*),用来支持溢出运算
var min = UInt8.min //0
//min -= 1//此时会运行时错误
min = min &- 1 //255 UInt8.max

var max = UInt8.max //255
//max += 1//此时会运行时错误
max = max &+ 1 //0 UInt8.min

max = UInt8.max
print(max &* 2)//max+1+254 = 254
溢出运算符图解

运算符重载

  • 类、结构体、枚举可以为现有的运算符提供自定义的实现,即运算符重载
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: 10) + Point(x: 20, y: 20)
print(p)//Point(x: 30, y: 30)

struct Size {
    var width: Int ,height: Int
    //如果需要写在结构体中 需要加上static
    static func + (_ s1: Size, _ s2: Size) -> Size {
        Size(width: s1.width + s2.width, height: s1.height + s2.height)
    }
}

let s = Size(width: 10, height: 10) + Size(width: 20, height: 20)
print(s)//Size(width: 30, height: 30)
struct Size {
    var width: Int ,height: Int
    //如果需要写在类、结构体中 需要加上static 因为这不是实例调用的函数
    static func + (_ s1: Size, _ s2: Size) -> Size {
        Size(width: s1.width + s2.width, height: s1.height + s2.height)
    }
    
    static func - (_ s1: Size, _ s2: Size) -> Size {
        Size(width: s1.width - s2.width, height: s1.height - s2.height)
    }
    
    static func += (_ s1: inout Size, _ s2: Size){
        s1 = s1 + s2
    }
    
    static prefix func ++ (_ s1: inout Size) -> Size {//前++运算符
        s1 += Size(width: 1, height: 1)
        return s1
    }
    
    static postfix func ++ (_ s1: inout Size) -> Size {//后++运算符
        let tmp = s1
        s1 += Size(width: 1, height: 1)
        return tmp
    }
    
    static func == (_ s1: Size, _ s2: Size) -> Bool { //判断相等
        return (s1.width == s2.width)&&(s1.height == s2.height)
    }
}

Equatable

  1. Equatable协议
  • 想确定2个实例是否等价,一般是遵守Equatable协议,重载==运算符
  • 与此同时,等价于重载了** != **运算符
  1. Swift提供默认的Equatable实现
  • 没有关联类型的枚举
  • 只遵守Equatable协议关联类型的枚举
  • 只遵守Equatable协议存储属性的结构体(即如果结构体中的某些实例属性未遵守,则不行)
  1. 引用类型比较存储的地址值是否相等(是否引用同一个对象) 使用(=== 、!==)
class Person : Equatable {//不遵守 重载==运算符也可以 
//作用:
//1、声明Person可以使用==判断 可以调用下面的equals函数
//2、自动重载 != 运算符
    var age: Int
    init(age: Int) {
        self.age = age
    }
    static func == (p1 :Person,p2 :Person) -> Bool {
        p1.age == p2.age
    }
}

var p1 = Person(age: 10)
var p2 = Person(age: 10)
print(p1 == p2)//true

//只有遵守Equatable协议的泛型才可以使用该函数
func equals<T :Equatable>(_ t1: T,_ t2: T) -> Bool{
    t1 == t2
}

print(equals(p1, p2))//true
//无关联类型的的枚举
enum Season: Equatable {
    case spring,summer,autoum,winter
}
var season1 = Season.winter
var season2 = Season.spring

print(season1 == season2)//false
print(season1 != season2)//true 自动重载!=运算符

//只遵守Equatable协议关联类型的枚举
enum Score: Equatable {
    case score(Int)
    case grade(String)
}

var score1 = Score.score(80)
var score2 = Score.score(90)

print(score1 == score2)//false
print(score1 != score2)//true 自动重载!=运算符


//只拥有遵守Equatable协议的存储属性的结构体
class Person {
    var age: Int
    init(age: Int) {
        self.age = age
    }
}

struct Point : Equatable{
//    static func == (lhs: Point, rhs: Point) -> Bool {//因为Person未遵守 所以需要重载==运算符
//        lhs.x == rhs.x &&
//        lhs.y == rhs.y &&
//        lhs.a == rhs.a &&
//        lhs.p.age == rhs.p.age
//    }
    //遵守Equatable的结构体
    var x: Int ,y: Int
    var a :Array = [Int]()//可想而知Int、Array等常用数据类型是遵守Equatable协议的
    //var p :Person = Person(age: 10)//如果其存储属性未遵守Equatable协议 则必须重载==运算符
    
}

var p1 = Point(x: 10, y: 20)
var p2 = Point(x: 10, y: 20)
print(p1 == p2)//true
print(p1 != p2)//false 自动重载!=运算符
var p1 = Person(age: 10)
var p2 = p1
print(p1 === p2)//引用类型比较存储的地址是否相等(是否引用同一个对象)
Comparable
  • 想要比较两个实例的大小:遵守Comparable协议,重载相应的运算符
//比较两个学生的能力 成绩优先 成绩相同,年龄小更优秀
struct Student : Comparable {
    
    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 age :Int
    var score :Int
}

var student1 = Student(age: 10, score: 69)
var student2 = Student(age: 12, score: 69)
var student3 = Student(age: 11, score: 89)
var student4 = Student(age: 13, score: 89)

print(student1 > student2) //true
print(student1 >= student3)//false
print(student3 < student4) //false
print(student3 <= student4)//false
print(student1 <= student4)//true
自定义运算符

在全局作用域中使用opretor进行声明

  • prefix opretor 前缀运算符
  • postfix opretor 后缀运算符
  • infix opretor 中缀运算符 : 优先级组(需要定义结合性、优先级等等)
自定运算符实例 --前后自减运算符 +- 前加后减
prefix operator --
postfix operator --

infix operator +- : PlusMinusPrecedence
precedencegroup PlusMinusPrecedence{
    associativity: none
    higherThan: AdditionPrecedence
    lowerThan: MultiplicationPrecedence
    assignment: true
}

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 prefix func  -- (p1:inout Point) -> Point{
        p1 = p1 - Point(x: 1, y: 1)
        return p1
    }
    
    static postfix func  -- (p1:inout Point) -> Point{
        let tmp = p1
        p1 = p1 - Point(x: 1, y: 1)
        return tmp
    }
    
    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 ?? 0 + p2.x, y: p1?.y ?? 0 - p2.y)
    }
}

var point1 = Point(x: 10, y: 20)
var point2 = Point(x: 11, y: 22)
print(--point1)//point1 : Point(x: 9, y: 19) 先计算,再输出 Point(x: 9, y: 19)
print(point1--)//point1 : Point(x: 8, y: 18) 先输出Point(x: 9, y: 19),再自减
print(--point1 - point1--)//结果为:Point(x: 0, y: 0) !!!会出现歧义,所以Swift去除自增自减是明智的

扩展(Extension)

什么是扩展
  1. Swift中的扩展,类似于OC中的分类(Category)
  2. 扩展可以为枚举、结构体、类、协议添加新功能
  • 可以添加方法、计算属性、下标、(便捷)初始化器、嵌套类型、协议等
  1. 扩展不能办到的事情(不能影响内存结构以及破坏安全性)
  • 不能覆盖原有功能(OC可以覆盖)
  • 不能添加存储属性 (OC通过runtime添加)
  • 不能添加父类
  • 不能添加指定初始化器,不能添加反初始化器
    ...
添加方法、计算属性、下标、嵌套类型
//新增计算属性
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 d = 100.0
print(d.km)//100000.0
print(d.m) //100.0
print(d.dm)//10.0
print(d.cm)//1.0
print(d.mm)//0.1

//新增下标
extension Array{
    //避免数组下标越界
    subscript(nullable idx: Int) -> Element?{
        if (startIndex..<endIndex).contains(idx){
            return self[idx]
        }
        return nil
    }
}

var arr = [10,20,30]
print(arr[nullable: 5])//输出:nil

//新增方法
extension Int {
    //重复某个方法
    func repeats(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 .positive
        }
    }
    
    //获取第*位数字
    subscript(digitIndex: Int) -> Int{
        var decimalBase = 1
        for _ in 0 ..< digitIndex{
            decimalBase *= 10
        }
        return (self / decimalBase) % 10
    }
}

var num = 10
num.repeats {
    print("hello!")//重复打印10次
}

print(num.square())//输出:100

print(num.kind)//输出:positive

print(num[2])//十位为1 输出: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 == (lhs: Person, rhs: Person) -> Bool {
        lhs.age == rhs.age && lhs.name == rhs.name
    }
    convenience init() {
        self.init(age:10,name:"盖伦")
    }
}

var p1 = Person()
print(p1.age,p1.name)//输出:10 盖伦


struct Point {
    var x: Int = 0
    var y: Int = 0
}

extension Point{
    init(_ point: Point) {
        self.init(x: point.x, y: point.y)
    }
}

//编译器生成默认初始化器,同时可以自定义初始化器
var po1 = Point()
var po2 = Point(x: 10)
var po3 = Point(y: 10)
var po4 = Point(x: 10,y: 10)
var po5 = Point(po4)
协议
  1. 如果一个类已经实现了协议中的所有要求,但是还没有声明它遵守了这个协议,则可以通过扩展让他遵守这个协议
  2. 扩展可以给协议提供默认实现,也间接实现「可选协议的效果」
  3. 扩展可以给协议扩从「协议中从未声明过的方法」
//示例1
protocol TestProcotol {
    func test()
}

class Person {
    func test() {
        print("test")
    }
}

extension Person :TestProcotol{

}
//示例1
//BinaryInteger 是Int UInt8 UInt16等共同遵循的协议
//方法一:
func isOdd<T: BinaryInteger>(_ i:T)-> Bool{
    i % 2 != 0
}
print(isOdd(5))//输出:true

//方法二:给协议扩展方法
extension BinaryInteger{
    func isOdd() -> Bool {
        self % 2 != 0
    }
}
print(5.isOdd())//输出:true
protocol TestProcotol {
    func func1()
}

class Person :TestProcotol {
    //如果扩展中没有实现,则必须实现协议中的方法 否则编译报错
    func func1() {
        print("PersonClass func1")
    }
    
    func func2() {
        print("PersonClass func2")
    }
}

//如果可以在协议扩展中实现或补充 继承该协议的类可以不用实现协议中的方法
extension TestProcotol{
    func func1() {
        print("extension func1")
    }
    func func2() {
        print("extension func2")
    }
}

var person :TestProcotol = Person()
//func1在扩展中已经实现
person.func1()//PersonClass func1
//func2在原协议中没有
person.func2()//extension func2

疑问:为什么func2()输出会不同呢?
如果person实例后面没有加TestProcotol 则输出为PersonClass func2 加了之后就是extension func2 因为原协议中没有声明func2,因此:
原协议中有的方法,优先实例对象中实现的方法
原协议中没有的方法,如果实例有声明该协议,则优先协议扩展中的方法;没有声明,优先自己

泛型
  • 扩展中依然可以使用原类型中的泛型类型
  • 泛型符合条件才扩展
class Stack <Element>{
    var elements = [Element]()
    init(_ numbers:Element...) {
        self.elements = numbers
    }
    func push(_ element:Element) {
        elements.append(element)
    }
    func pop() -> Element {
        elements.removeLast()
    }
    
}

//泛型中依然可以使用原类型中的扩展
extension Stack{
    func top() -> Element {
        elements.last!
    }

    func size() -> Int {
        elements.count
    }
}

//符合条件才扩展
extension Stack where Element : Equatable{
    static func == (left:Stack,right:Stack) -> Bool {
        left.elements == right.elements
    }
}

var s1 = Stack(10,20,30)
var s2 = Stack(10,20,30)
print(s1 == s2)//true

var s3 = Stack(Stack(10),Stack(20),Stack(30))//元素不符合
var s4 = Stack(10,20,30)
print(s3 == s4)//元素不符合 编译报错 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容