Optioanl 是通过 enum 实现的⼀个⾮常好的代表,这⾥我们可以通过阅读 Optional 的源码来看⼀下:
@frozen
public enum Optional: ExpressibleByNilLiteral {
// The compiler has special knowledge of Optional, including the fact
// that it is an `enum` with cases named `none` and `some`.
/// The absence of a value.
///
/// In code, the absence of a value is typically written using the `nil`
/// literal rather than the explicit `.none` enumeration case.
case none
/// The presence of a value, stored as `Wrapped`.
case some(Wrapped)
...
}
可以看到这⾥ Optional 的本质是⼀个 enum 。当前枚举接收⼀个泛型参数,⽽当前 some 的关联值 是当前的 Wrapped ,也就是说下⾯两种写法是完全等价的:
var age: Int? = 10
var age1: Optional = Optional(10)
同样的,既然是枚举,那么我们是可以通过模式匹配来匹配对应的值
switch age{
case nil:
print("nil")
case .some(10):
print("10")
case .some(_):
print("other")
}
涉及到 Optional 我们就不得不⾯对⼀个问题:解包。因为当前的可选项其实是对我们的值做了包装, 当前不为 nil 的时候我们就需要从其中拿到我们要的值。
⽐较粗暴的⽅式是:强制解包,⽐如下⾯这段代码
var age: Int? = 10
print(age!)
这样做的好处是省事,坏处是当前⼀旦 age 是nil ,那么应⽤程序就会崩溃
这⾥⽐较好的做法就是使⽤ if let
或者 guard let
.这⾥我们是通过可选项绑定的⽅式来判断当前 的可选项是否有值.
if let tmp = age{
print("\(tmp)")
}else{
print("nil")
}
guard let tmp = age else {
print("age 为空")
return
}
print(tmp)
可以看到 gurad
后⾯的判断条件为 false
的时候会执⾏当前⼤括号⾥⾯的内容,反之执⾏后⾯的代码。gurad
在我们当前这句代码⾥⾯扮演的⻆⾊就是如果当前为空,我们就退出我们当前的递归调⽤。 其次,⼤家来看⼀个点:我们在使⽤if let
创建的内容当中 tmp
仅仅只能在当前 if 分⽀的⼤括号内访问到,⽽我们当前的guard
定义的 tmp
在当前⼤括号外部也是能访 问到的。
Equatable
Swift中的类型,可以通过遵循 Equatable
协议来使⽤相等运算符(==)和不等运算符(!=)来⽐较两 个值相等还是不相等。Swift 标准库中绝⼤多数的类型都默认实现了 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
}
}
}
如果我们对于⾃定的类型实现 ==
,我们需要实现Equatable
协议
struct Teacher :Equatable{
var age: Int
var name: String
}
var t1 = Teacher(age: 18, name: "a")
var t2 = Teacher(age: 18, name: "a")
print(t1 == t2)
我们实现Equatable
协议就会自动帮我们实现==
方法,我们可以通过sil文件查看
struct Teacher : Equatable {
@_hasStorage var age: Int { get set }
@_hasStorage var name: String { get set }
init(age: Int, name: String)
@_implements(Equatable, ==(_:_:)) static func __derived_struct_equals(_ a: Teacher, _ b: Teacher) -> Bool
}
// static Teacher.__derived_struct_equals(_:_:)
sil hidden @$s4main7TeacherV23__derived_struct_equalsySbAC_ACtFZ : $@convention(method) (@guaranteed Teacher, @guaranteed Teacher, @thin Teacher.Type) -> Bool {
// %0 // users: %13, %6, %3
// %1 // users: %15, %7, %4
// %2 // user: %5
bb0(%0 : $Teacher, %1 : $Teacher, %2 : $@thin Teacher.Type):
debug_value %0 : $Teacher, let, name "a", argno 1 // id: %3
debug_value %1 : $Teacher, let, name "b", argno 2 // id: %4
debug_value %2 : $@thin Teacher.Type, let, name "self", argno 3 // id: %5
%6 = struct_extract %0 : $Teacher, #Teacher.age // user: %8
%7 = struct_extract %1 : $Teacher, #Teacher.age // user: %9
%8 = struct_extract %6 : $Int, #Int._value // user: %10
%9 = struct_extract %7 : $Int, #Int._value // user: %10
%10 = builtin "cmp_eq_Int64"(%8 : $Builtin.Int64, %9 : $Builtin.Int64) : $Builtin.Int1 // user: %11
cond_br %10, bb1, bb4 // id: %11
///如果当前 age 的值⽐较成功了,就跳转 bb1 ,否则跳转 bb4 ,我们先来看 bb1 的代码分⽀:
bb1: // Preds: bb0
%12 = metatype $@thin String.Type // user: %18
%13 = struct_extract %0 : $Teacher, #Teacher.name // users: %20, %18, %14
retain_value %13 : $String // id: %14
%15 = struct_extract %1 : $Teacher, #Teacher.name // users: %19, %18, %16
retain_value %15 : $String // id: %16
// function_ref static String.== infix(_:_:)
%17 = function_ref @$sSS2eeoiySbSS_SStFZ : $@convention(method) (@guaranteed String, @guaranteed String, @thin String.Type) -> Bool // user: %18
%18 = apply %17(%13, %15, %12) : $@convention(method) (@guaranteed String, @guaranteed String, @thin String.Type) -> Bool // user: %21
release_value %15 : $String // id: %19
release_value %13 : $String // id: %20
%21 = struct_extract %18 : $Bool, #Bool._value // user: %22
cond_br %21, bb2, bb3 // id: %22
bb2: // Preds: bb1
%23 = integer_literal $Builtin.Int1, -1 // user: %24
%24 = struct $Bool (%23 : $Builtin.Int1) // user: %25
br bb5(%24 : $Bool) // id: %25
bb3: // Preds: bb1
%26 = integer_literal $Builtin.Int1, 0 // user: %27
%27 = struct $Bool (%26 : $Builtin.Int1) // user: %28
br bb5(%27 : $Bool) // id: %28
bb4: // Preds: bb0
%29 = integer_literal $Builtin.Int1, 0 // user: %30
%30 = struct $Bool (%29 : $Builtin.Int1) // user: %31
br bb5(%30 : $Bool) // id: %31
// %32 // user: %33
bb5(%32 : $Bool): // Preds: bb2 bb3 bb4
return %32 : $Bool // id: %33
} // end sil function '$s4main7TeacherV23__derived_struct_equalsySbAC_ACtFZ'
然而如果是我们的类中就需要手动实现了
static func == (lhs: Teacher, rhs: Teacher) -> Bool {
return lhs.age == rhs.age && lhs.name == rhs.name
}
===
是⽤来检验两个对象是否是同⼀个实例对象是否是同⼀个
空运算符(??)
var age:Int? = nil
print(age ?? 20)
通过源码可以看到空运算符有两种
@_transparent
public func ?? (optional: T?, defaultValue: @autoclosure () throws -> T)
rethrows -> T {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
@_transparent
public func ?? (optional: T?, defaultValue: @autoclosure () throws -> T?)
rethrows -> T? {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
根据空运算符的返回值来确定
var age:Int? = nil
var age1:Int? = 20
var t = age ?? age1
这个时候t
的类型是不是可选类型由age1
决定
可选链
class Teacher {
var age: Int?
var subject: Subject?
}
class Subject{
var subName :String?
func test(){}
}
var t = Teacher()
if let tmp = t.subject?.subName{
}
t.subject?.test()
如果发现可选值为空的时候就不会往下执行了
unsafelyUnwrapped
unsafelyUnwrapped
和强制解包效果一样
t.subject!
t.subject.unsafelyUnwrapped
都不会检测是否为空
类型转换(as)
var age:Int = 10
var age1 = age as AnyObject
as
可以转换为另一个类型
as?
:转换失败返回nil
as!
:转换失败直接奔溃