• swift枚举(三)-Optional


    swift枚举(一)

    swift枚举(二)

    认识可选值

    之前我们在写代码过程中早就接触过可选值,比如我们在代码中这样定义:

    	class IFLPerson {
    		var name: String?
    	}
    
    • 1
    • 2
    • 3

    当前的name,我们就称之为可选值

    var name: String? == var name: Optional

    这两种写法是等同的

    nameOptional的本质是什么,我们直接跳转到 源码 打开 Optional.swift 文件

    在这里插入图片描述

    既然Optional本质是枚举,那么我们也可以实现一个自定义的Optional

    比如给定任一自然数,如果自然数是奇数则返回,否则返回nil, 该如何设计

    	enum IFLOptional {
    	    case some(Value)
    	    case none
    	}
    	
    	func getOddValue(_ value: Int) -> IFLOptional {
    	    if value % 2 == 0 {
    	        return .none
    	    }
    	    return .some(value)
    	}
    	
    	// 给定一个数组,删除数组中的所有奇数
    	var array = [1, 2, 3, 4, 5, 6, 7, 8, 101]
    	for elem in array {
    	    let value = getOddValue(elem)
    	    switch value {
    	    case let .some(value):
    	        array.remove(at: array.firstIndex(of: value)!)
    	    case .none:
    	        print("\(value) not exist")
    	    }
    	}
    	
    	print("-------------")
    	print(array)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    结果

    [2, 4, 6, 8]

    如果我们把上面的 getOddValue 返回值更换一下,其实就和系统的Optional 使用没什么差别

    	func getOddValue(_ value: Int) -> Int? {
    	    if value % 2 == 0 {
    	        return .none
    	    }
    	    return .some(value)
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其实就是利用当前编译器的类型检查来达到语法书写层面的安全性

    当然如果每一个可选值都用模式匹配的方式来获取值,在代码书写上就比较繁琐,

    我们还可以使用 if let 的方式来进行可选值绑定

    	if let value = value {
    		array.remove(at: array.firstIndex(of: value))
    	}
    
    • 1
    • 2
    • 3

    还可以使用 guard let

    如果 if let 凭空多了一个分支,guard let 是降低分支层次的办法

    可选链

    我们都知道在OC 中我们给一个nil 对象发送消息什么也不会发生

    Swift中我们是没办法向一个nil对象直接发送消息的,但是借助可选链可以达到类似的效果

    	let str: String? = "abc"
    	let upperStr = str?.uppercased() // Optional<"ABC">
    	var str1: String?
    	let upperStr1 = str1?.uppercased() // nil
    	print(upperStr)
    	print(upperStr1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结果

    Optional(“ABC”)

    nil

    再来看下面这段代码输出什么

    	let str: String? = "aBc"
    	let lowerStr = str?.uppercased().lowercased()
    	var str1: String?
    	let lowerStr1 = str1?.uppercased().lowercased()
    	print(lowerStr)
    	print(lowerStr1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结果

    Optional(“abc”)

    nil

    同样的 可选链对于下标和函数调用 也适用

    	var closure: ((Int) -> ())?
    	closure?(1)		// closure为nil 不执行
    
    • 1
    • 2

    closure为nil 不执行

    	let dict: [String: Any]? = ["key1": 1, "key2": 2]
    	print(dict?["key1"])
    	print(dict?["key3"])
    
    • 1
    • 2
    • 3

    结果

    Optional(1)

    nil

    ?? 运算符 (空合并运算符)

    ( a ?? b ) 将对可选类型 a 进行空判断,如果 a 包含一个值就进行解包,否则就返回 一个默认值 b .

    • 表达式 a 必须是 Optional 类型
    • 默认值 b 的类型必须要和 a 存储值的类型保持一致

    运算符重载

    在源码中我们可以看到除了重载了 ?? 运算符,

    Optional 类型还重载了 == , ?= 等 等运算符,实际开发中我们可以通过重载运算符简化我们的表达式

    在这里插入图片描述

    比如在开发中我们定义了一个二维向量,这个时候我们想对两个向量进行基本的操作,

    那么我们就可以通过重载运算符来达到我们的目的

    	struct Vector {
        
    	    let x: Int
    	    let y: Int
    	}
    	extension Vector {
    	    static func + (firstVector: Vector, secondVector: Vector) -> Vector {
    	        return Vector(x: firstVector.x + secondVector.x, y: firstVector.y + secondVector.y)
    	    }
    	    static prefix func - (vector: Vector) -> Vector {
    	        return Vector(x: -vector.x, y: -vector.y)
    	    }
    	    static func - (firstVector: Vector, secondVector: Vector) -> Vector {
    	        return firstVector + -secondVector
    	    }
    	}
    	
    	let v1 = Vector(x: 5, y: 10)
    	let v2 = Vector(x: 3, y: 4)
    	let v3 = v1 + v2
    	let v4 = v1 - v2
    	let v5 = -v1
    	print(v3)
    	print(v4)
    	print(v5)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    结果

    Vector(x: 8, y: 14)

    Vector(x: 2, y: 6)

    Vector(x: -5, y: -10)

    隐士解析可选类型

    隐式解析可选类型是可选类型的一种,使用的过程中和非可选类型无异

    它们之间唯一 的区别是,隐式解析可选类型是你告诉 Swift 编译器,在运行时访问时,值不会 为 nil, 但是如果你导致nil值,运行报错

    	var age: Int
    	var age1: Int!
    	age = nil		// 报错 'nil' cannot be assigned to type 'Int'
    	age1 = nil
    
    • 1
    • 2
    • 3
    • 4
    	var age: Int?
    	var age1: Int!
    	age = nil		// 改为可选值 就可以 赋值nil了
    	age1 = nil
    
    	let x = age1 % 2  // 这里不需要做解包的操作,编译器已经帮我们做了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其实日常开发中,我们比较常见的一种隐士可选类型

    @IBOutlet weak var button: UIButton!

    IBOutlet类型是Xcode强制为可选类型的,因为它不是在初始化时赋值的,而是在加载视图的时候

    你可以把它设置为普通的可选类型,但是如果这个视图加载正确,它是不会为空的

    与可选值有关的高阶函数

    • map : 这个方法接受一个闭包,如果可选值有内容则调用这个闭包进行转换
    	var dict = ["one": "1", "two": "2"] 
    	let result = dict["one"].map{ Int($0) } 
    	print(result)
    
    • 1
    • 2
    • 3

    结果

    Optional(Optional(1))

    上面的代码中我们从字典中取出字符串”1”,并将其转换为Int类型,

    但因为String转换成 Int不一定能成功,所以返回的是Int?类型,

    而且字典通过键不一定能取得到值,所以map 返回的也是一个Optional,

    所以最后上述代码result的类型为 Int?? 类型

    那么如何把我们的双重可选展平开来,这个时候我们就需要使用到

    • flatMap: 可以把结果展平成为单个可选值
    	var dict = ["one": "1", "two": "2"] 
    	let result = dict["one"].flatMap{ Int($0) } 
    	print(result)
    
    • 1
    • 2
    • 3

    结果

    Optional(1)

    • 注意,这个方法是作用在Optional的方法,而不是作用在 Sequence上的
    • 作用在Sequence上的flatMap 在 Swift4.1 中被更名为 compactMap, 该方法可以将序列中的nil过滤出去
    	let array1 = ["1", "2", "3", nil]
    	let result2 = array1.compactMap {
    	    $0
    	}
    	print(result2)
    	
    	let array2 = ["1", "2", "3", "four"]
    	let result3 = array2.compactMap {
    	    Int($0)
    	}
    	print(result3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果

    [“1”, “2”, “3”]

    [1, 2, 3]

    元类型、AnyClass、Self (self)

    • AnyObject: 代表任意类的 instance,类的类型,仅类遵守的协议
    • Any: 代表任意类型,包括 funcation 类型或者 Optional 类型
    • AnyClass: 代表任意实例的类型
  • 相关阅读:
    数据结构--》掌握数据结构中的查找算法
    一个基于.Net Core跨平台小程序考试系统
    ERP采购管理系统软件
    centos8手动编译安装swoole过程
    Js写的二级联动和三级联动
    基于单片机的智能家居安保系统(论文+源码)
    惯性导航定位技术
    JS,事件试题,在实践中应用,非常详细!!
    mysql数据库主从同步
    【TestNG学习(三)套件测试】
  • 原文地址:https://blog.csdn.net/qq_42431419/article/details/127973168