Swift探索10:Optional和amp;Equatable和amp;Comparable和amp;访问控制权限
本文主要分析Optional源码、Equatable Comparable协议
Optional分析
swift中的可选类型(Optional)
,用于处理值缺失
的情况,有以下两种情况
-
有值,且等于x
-
没有值
这点可以通过swift-source->Optional.swift
源码(CMD P
,搜索Optional)源码来印证
-
@frozen
-
public enum Optional<Wrapped>: ExpressibleByNilLiteral {
-
......
-
//为nil
-
case none
-
-
......
-
//有值
-
case some(Wrapped)
-
-
......
-
}
-
通过源码可知,
Optional
的本质是enum
,当前枚举接收一个泛型参数Wrapped
,当前Some的关联值
就是当前的Wrapper
,下面两种写法完全等价
-
var age: Int? = 10
-
等价于
-
var age1: Optional<Int> = Optional(5)
-
【Optional使用模式匹配】:既然
Optional
的本质是枚举,那么也可以使用模式匹配
来匹配对应的值,如下所示
-
//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")
-
}
-
【Optional解包】:因为是
Optional
类型,当我们需要从其中拿到我们想要的值时,需要对其进行解包
,因为当前的可选项是对值做了一层包装的,有以下两种方式:-
if let
:如果有值,则会进入if流程 -
guard let
:如果为nil,则会进入else流程 -
1、
强制解包
:好处是省事,坏处是一旦解包的值是nil,那么程序就会崩溃强制解包为nil崩溃
-
2、通过
可选项绑定
:判断当前的可选项是否有值
-
-
//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)
可选项绑定总结
-
1、使用
if let
创建的内容当中age仅仅只能在当前if分支的大括号内访问 -
2、使用
guard let
定义的tmp在当前大括号外部也是能访问的
Equatable协议
在上面的例子中,可以通过==判断两个可选项是否相等,原因是因为Optinal在底层遵循了Equatable
协议
-
var age: Int? = 10
-
var age1: Optional<Int> = Optional(5)
-
-
age == age1
-
继续回到
Optional
源码中,可以看到Optional遵循了Equatable
协议
-
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
协议,系统默认帮我们实现了==方法
-
查看
SIL
方法,是否如我们猜想的一样?经过验证确实与我们猜测结论是一致的SIL验证-1
-
查看
__derived_struct_equals
方法的实现SIL验证-2
-
疑问:如果是Class类型呢?
如果像Struct
那么写,会报错,提示需要自己实现Equatable
协议的方法
class仅遵守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)
-
如果class中的age和name都是
Optional
呢?
-
//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 ===
-
==
相当于 equal to,用于判断两个值是否相等
-
===
是用来判断两个对象是否是同一个实例对象
(即内存地址指向是否一致)
-
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重写 < 运算符
-
以struct为例,遵循
Comparable协议
,重写 < 运算符
-
//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
-
进入
Optional
源码,查看??
实现
-
<!--返回T-->
-
//空运算符
-
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?-->
-
-
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
,一种是与
,主要是与 ?? 后面的返回值有关
(即简单来说,就是??后是什么类型,??返回的就是什么类型
),如下所示
-
??后面是age1,而age1的类型是Int?,所以t的类型是
Int?
??是Int?
-
如果??是30呢? -- 类型是
Int
??是30
-
如果??是String呢? -- 会报错,??要求类型一致(跟是否是可选类型无关)
??是String
可选链
可选链 则意味着 允许在一个链上来访问当前的属性/方法,如下所示
-
//***************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!
-
as
将类型转换为其他类型
-
var age: Int = 10
-
-
var age1 = age as Any
-
print(age1)
-
-
var age2 = age as AnyObject
-
print(age2)
-
-
<!--打印结果-->
-
10
-
10
-
as?
将类型转换为其他可选类型
-
var age: Int = 10
-
//as?
-
//as? 不确定类型是Double,试着转换下,如果转换失败,则返回nil
-
var age3 = age as? Double
-
print(age3)
-
-
<!--打印结果-->
-
nil
此时的age3的类型是Double?
as?运行结果
-
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分析
-
常规使用:如果可以明确类型,则可以直接使用as!
-
//常规使用
-
var age: Any = 10
-
func test(_ age: Any) -> Int{
-
return (age as! Int) 1
-
}
-
print(test(age))
-
-
<!--打印结果-->
-
11
使用建议
-
如果
能确定
的类型,使用as!
即可 -
如果是
不能确定
的,使用as?
即可
总结
-
Optional
的本质是enum
,所以可以使用模式匹配
来匹配Optional的值 -
Optional的
解包方式
有两种:-
1、
强制解包
:一旦为nil,程序会崩溃 -
2、
可选值绑定
:if let
(只能在if流程的作用域内访问)、guard let
-
-
Equatable协议:
-
对于
swift标准库中的绝大部分类型
都默认实现了Equatable
协议 -
对于自定义
Struct
类型,仅需要遵守Equatable
协议 -
对于自定义
class
类型,除了需要遵守Equatable
协议,还需要自己实现Equatable
协议的方法
-
-
区分 == vs ===
-
==
相当于 equal to,用于判断两个值是否相等
-
===
是用来判断两个对象是否是同一个实例对象
(即内存地址指向是否一致)
-
-
Comparable协议:
-
对于自定义类型,需要遵循
Comparable
协议,并重写运算符
-
??
空运算符:??只有两种类型,一种是T
,一种是T?
,主要是与 ?? 后面的返回值有关(即简单来说,就是??后是什么类型,??返回的就是什么类型
)
-
-
可选链:允许在一个链上来访问当前的属性/方法,如果
为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
:最不受限制的访问级别,可以在任意地方、任意模块间被继承、定义、重写
-
-
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgicfhj
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13