本文主要分析Optional源码、Equatable+Comparable协议
Optional分析
swift中的可选类型(Optional) ,用于处理值缺失 的情况,有以下两种情况
这点可以通过swift-source->Optional.swift 源码(CMD+P ,搜索Optional)源码来印证
@frozen
public enum Optional<Wrapped>: ExpressibleByNilLiteral {
......
//为nil
case none
......
//有值
case some(Wrapped)
......
}
var age: Int? = 10
等价于
var age1: Optional<Int> = Optional(5)
//1、声明一个可选类型的变量
var age: Int? = 10
//2、通过模式匹配来匹配对应的值
switch age{
case nil:
print("age 是个空值")
case .some(let val):
print("age的值是\(val)")
}
<!--或者这样写-->
switch age{
case nil:
print("age 是个空值")
case .some(10):
print("age的值是10")
default:
print("unKnow")
}
//3、可选项解包
var age: Int? = nil
//3-1、强制解包
//如果age为nil,则程序崩溃
print(age!)
//3-2、可选值绑定
<!--方式一-->
if let age = age{
//如果age不为nil,则打印
print(age)
}
<!--方式二-->
guard let tmp = age else {
print("age为nil")
return
}
print(tmp)
可选项绑定总结
Equatable协议
在上面的例子中,可以通过==判断两个可选项是否相等,原因是因为Optinal在底层遵循了Equatable 协议
var age: Int? = 10
var age1: Optional<Int> = Optional(5)
age == age1
extension Optional: Equatable where Wrapped: Equatable {
......
@inlinable
public static func ==(lhs: Wrapped?, rhs: Wrapped?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l == r
case (nil, nil):
return true
default:
return false
}
}
}
swift标准库中的类型
在swift中的类型,可以通过遵循Equatable 协议来使用相等运算符(==) 和不等运算符(!=) 来比较两个值相等还是不相等 ,Swift标准库中绝大多数类型都默认实现了Equatable 协议
例如下面的例子,对于Int类型来说,系统默认实现了?==
var age2: Int = 20
var isEqual = age1 == age2
print(isEqual)
<!--打印结果-->
false
自定义类型
对于自定义的类型 ,如果想实现 == ,应该怎么办呢?
-
如果像下面这样写,是会直接报错的 报错示意 -
可以通过遵循Equatable协议实现 ,如下所示
//2、自定义类型如何实现Equatable协议
struct CJLTeacher: Equatable{
var age: Int
var name: String
}
var t = CJLTeacher(age: 18, name: "CJL")
var t1 = CJLTeacher(age: 19, name: "CJL")
print(t == t1)
<!--打印结果-->
false
//如果将t1中的age改成18,打印结果是什么
true
为什么呢?其根本原因是因为遵守了Equatable 协议,系统默认帮我们实现了==方法
疑问:如果是Class类型呢?
如果像Struct 那么写,会报错,提示需要自己实现Equatable 协议的方法
class仅遵守Equatable协议报错
//3、如果是class类型呢?需要手动实现Equatable协议的方法
class CJLTeacher: Equatable{
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
static func == (lhs: CJLTeacher, rhs: CJLTeacher) -> Bool {
return lhs.age == rhs.age && lhs.name == rhs.name
}
}
var t = CJLTeacher(age: 18, name: "CJL")
var t1 = CJLTeacher(age: 19, name: "CJL")
print(t == t1)
//4、如果class中的属性都是可选类型呢?底层是调用Optional的==来判断
class CJLTeacher: Equatable{
var age: Int?
var name: String?
init(age: Int, name: String) {
self.age = age
self.name = name
}
static func == (lhs: CJLTeacher, rhs: CJLTeacher) -> Bool {
return lhs.age == rhs.age && lhs.name == rhs.name
}
}
var t = CJLTeacher(age: 18, name: "CJL")
var t1 = CJLTeacher(age: 19, name: "CJL")
print(t == t1)
查看其SIL文件可以验证这一点:底层是通过调用Optional的==来判断
class的SIL验证
区分 == vs ===
class CJLTeacher: Equatable{
var age: Int?
var name: String?
init(age: Int, name: String) {
self.age = age
self.name = name
}
static func == (lhs: CJLTeacher, rhs: CJLTeacher) -> Bool {
return lhs.age == rhs.age && lhs.name == rhs.name
}
}
//===:判断两个对象是否是同一个
var t = CJLTeacher(age: 18, name: "CJL")
var t1 = t
t1.age = 20
print(t == t1)
<!--打印结果-->
true
除了==,还有!=以及其他的运算符
Comparable协议
除了Equatable,还有Comparable协议 ,其中的运算符有:< 、<=、>=、> 、...、..<、 等
public protocol Comparable : Equatable {
static func < (lhs: Self, rhs: Self) -> Bool
static func <= (lhs: Self, rhs: Self) -> Bool
static func >= (lhs: Self, rhs: Self) -> Bool
static func > (lhs: Self, rhs: Self) -> Bool
}
extension Comparable {
public static func ... (minimum: Self, maximum: Self) -> ClosedRange<Self>
......
}
Struct重写 < 运算符
//1、struct遵守Comparable协议
struct CJLTeacher: Comparable{
var age: Int
var name: String
//重载 < 符号
static func < (lhs: CJLTeacher, rhs: CJLTeacher) -> Bool {
return lhs.age < rhs.age
}
}
var t = CJLTeacher(age: 18, name: "CJL")
var t1 = CJLTeacher(age: 19, name: "CJL")
print(t < t1)
<!--打印结果-->
true
?? 空运算符
如果当前的变量为nil,可以在??返回一个nil时的默认值
//?? 空运算符
var age: Int? = nil
//?? 等价于 if le / guard let
print(age ?? 20)
<!--打印结果-->
20
<!--返回T-->
@_transparent//空运算符
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T)
rethrows -> T {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
<!--返回T?-->
@_transparent
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?)
rethrows -> T? {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
从源码中分析,?? 只有两种类型,一种是T ,一种是与 ,主要是与 ?? 后面的返回值有关 (即简单来说,就是??后是什么类型,??返回的就是什么类型 ),如下所示
可选链
可选链 则意味着 允许在一个链上来访问当前的属性/方法,如下所示
//***************6、可选链***************
class CJLTeacher{
var name: String?
var subject: CJLSubject?
}
class CJLSubject {
var subjectName: String?
func test(){print("test")}
}
var s = CJLSubject()
var t = CJLTeacher()
//可选链访问属性
if let tmp = t.subject?.subjectName{
print("tmp不为nil")
}else{
print("tmp为nil")
}
//可选链访问方法
t.subject?.test()
运行结果如下,因为s为nil,所以属性和方法都不会往下执行
可选链为nil的运行结果
unsafelyUnwrapped(Optional.swift中的)
这个和强制解包的内容是一致的,如下所示
//***************7、unsafelyUnwrapped 和强制解包内容是一致的
var age: Int? = 30
print(age!)
print(age.unsafelyUnwrapped)
<!--打印结果-->
30
30
//***************如果age是nil
var age: Int? = 30
print(age!)
print(age.unsafelyUnwrapped)
age是nil的结果和强制解包一致,程序会崩溃
unsafelyUnwrapped为nil的崩溃示意
-
官方对其的描述如下 unsafelyUnwrapped官方说明 这里的-O ,是指target -> Build Setting -> Optimization Level 设置成-O时,如果使用的是age.unsafelyUnwrapped ,则不检查这个变量是否为nil, -
1、设置Optimization Level ?为Fastest, Smallest[-Os] -
2、edit Scheme -> Run -> Info -> Build Configuration 改为release 模式,然后再次运行发现,没有崩溃,与官方所说是一致的 根据官方描述调测
区分as、 as? 和 as!
var age: Int = 10
var age1 = age as Any
print(age1)
var age2 = age as AnyObject
print(age2)
<!--打印结果-->
10
10
var age: Int = 10
//as?
//as? 不确定类型是Double,试着转换下,如果转换失败,则返回nil
var age3 = age as? Double
print(age3)
<!--打印结果-->
nil
此时的age3的类型是Double?
as?运行结果
var age: Int = 10
//as! 强制转换为其他类型
var age4 = age as! Double
print(age4)
运行结果如下,会崩溃
as!崩溃运行结果
SIL分析
查看以下代码的SIL文件
var age: Int = 10
var age3 = age as? Double
var age4 = age as! Double
as的SIL分析
//常规使用
var age: Any = 10
func test(_ age: Any) -> Int{
return (age as! Int) + 1
}
print(test(age))
<!--打印结果-->
11
使用建议
-
如果能确定 的类型,使用?as! ?即可 -
如果是不能确定 的,使用?as? ?即可
总结
-
Optional 的本质是enum ,所以可以使用模式匹配 来匹配Optional的值 -
Optional的解包方式 有两种:
-
Equatable协议:
-
对于swift标准库中的绝大部分类型 都默认实现了Equatable 协议 -
对于自定义Struct 类型,仅需要遵守Equatable 协议 -
对于自定义class 类型,除了需要遵守Equatable 协议,还需要自己实现Equatable 协议的方法
-
区分 == vs ===
-
Comparable协议:
-
可选链:允许在一个链上来访问当前的属性/方法,如果为nil ,则不会执行?后的属性/方法 -
unsafelyUnwrapped :与强制解包类似,但是如果项目中设置target -> Build Setting -> Optimization Level 设置成-O时,如果使用的是age.unsafelyUnwrapped ,则不检查这个变量是否为nil -
区分 as、as?、 as!
-
as ?将类型转换为其他类型 -
as? ?将类型转换为?其他可选类型 -
as! ?强制转换为其他类 -
//8-1、private:访问级别`仅在当前定义的作用域内有效
class CJLTeacher{
static let shareInstance = CJLTeacher()
private init(){}
}
var t = CJLTeacher.shareInstance
filePrivate filePrivate :访问限制仅限制在当前定义的源文件中 <!--1、在access.swift文件中定义CJLPartTimeTeacher-->
fileprivate class CJLPartTimeTeacher: CJLTeacher{
var partTime: Double?
init(_ partTime: Double) {
super.init()
self.partTime = partTime
}
}
<!--2、在main.swift中无法访问CJLPartTimeTeacher-->
调用报错提示 在access.swift文件中定义一个CJLPartTimeTeacher 全局变量,系统报错,其主要原因是 pt 默认的权限是 Internal的,而CJLPartTimeTeacher 的访问权限是fileprivate 的,pt的权限大于CJLPartTimeTeacher ,系统不允许这样,所以提示错误 filePrivate报错提示 -
需要使用private / fileprivate 修饰pt -
private let pt = CJLPartTimeTeacher(10.0)
//或者
fileprivate let pt = CJLPartTimeTeacher(10.0)
如果是直接定义在方法中的,可以不用访问权限修饰符 func test(){
let pt = CJLPartTimeTeacher(10.0)
}
Internal Internal :默认访问级别,允许定义模块中的任意源文件访问,但不能被该模块之外的任何源文件访问(例如 import Foundation,其中Foundation就是一个模块) <!--1、main.swift-->
import Foundation
class CJLTeacher{
init(){}
}
let t = CJLTeacher()
<!--2、custom-->
import AppKit
//访问main.swift中t,报错:Expressions are not allowed at the top level
print(t)
Internal报错提示 报错的主要原因是t 在main.swift 文件中的默认权限是Internal ,只能在同一个模块内使用,其属于Foundation 模块,而custom.swift 文件中不能调用t,是因为其属于AppKit 模块,与Foundation 并不是同一个模块,所以不能访问 public public :开放式访问,使我们能够在其定义模块的任何源文件中使用代码,并且可以从另一个源文件访问源文件。但是只能在定义的模块中继承和子类重写 open open :最不受限制的访问级别,可以在任意地方、任意模块间被继承、定义、重写 public与open的区别: -
public不可继承 -
open可继承 -
总结: -
没有写访问控制权限关键字时,默认 的访问权限是internal -
访问控制权限从高到低 的顺序:open > public > internal > filePrivate > private
-
1、private :访问级别仅在当前定义的作用域内有效 -
2、filePrivate :访问限制仅限制在当前定义的源文件中 -
3、Internal :默认访问级别,允许定义模块中的任意源文件访问,但不能被该模块之外的任何源文件访问 -
4、public :开放式访问,使我们能够在其定义模块的任何源文件中使用代码,并且可以从另一个源文件访问源文件。但是只能在定义的模块中继承和子类重写 -
5、open :最不受限制的访问级别,可以在任意地方、任意模块间被继承、定义、重写
|