在Optional.Swift
源码中,Optional
定义如下。
-
Optional
是通过enum
实现的,Optional
本质是枚举。 - 有两个
case
,nil
和some
。 - 关联值就是传进来的值。
本质上?
是语法糖,下面的写法等价:
var age: Int? = 10
var age1:Optional<Int> = Optional(10)
switch age {
case .none:
print("nil")
case .some(10):
print("\(10)")
default:
print("unKnown")
}
print(age == age1)
10
true
强制解包
var age: Int? = nil
print(age!)
如果age
没有值!
会直接crash
。Fatal error: Unexpectedly found nil while unwrapping an Optional value:
if let/guard let 可选绑定
if let
var age: Int? = nil
//相当于把age变量的值拿出来给到temp
if let temp = age {
print("\(temp)")
} else {
print("nil")
}
temp
作用域在if
中。
guard let
var age: Int? = 10
func test() -> Any {
guard let temp = age else {
return "error"
}
print(temp)
return temp
}
test()
10
temp
作用和guard
同级,可以在guard
外部访问到temp
。
init
@_transparent
public init(_ some: Wrapped) { self = .some(some) }
@_transparent
public init(nilLiteral: ()) {
self = .none
}
.some(some)
给了self
。
Equatable
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
}
}
}
前面age
和age1
能够比较就是因为Optional
遵循了Equatable
协议重载了==
运算符。
var age: Int? = 10
var age1:Optional<Int> = Optional(10)
print(age == age1)
如果我们自定义数据类型要进行==
需要遵循Equatable
协议:
struct HotpotCat {
var age: Int
var name: String
}
extension HotpotCat: Equatable {}
var hp = HotpotCat(age: 18, name: "hotpot")
var hp1 = HotpotCat(age: 18, name: "hotpot")
print(hp == hp1)
true
在HotpotCat
中我们并没有实现==
方法,编译器帮我们默认实现了,看下SIL
实现:
__derived_struct_equals
实现:
// static HotpotCat.__derived_struct_equals(_:_:)
sil hidden @static main.HotpotCat.__derived_struct_equals(main.HotpotCat, main.HotpotCat) -> Swift.Bool : $@convention(method) (@guaranteed HotpotCat, @guaranteed HotpotCat, @thin HotpotCat.Type) -> Bool {
// %0 "a" // users: %13, %6, %3
// %1 "b" // users: %15, %7, %4
// %2 "self" // user: %5
bb0(%0 : $HotpotCat, %1 : $HotpotCat, %2 : $@thin HotpotCat.Type):
//两个变量分别给到a和b(HotpotCat结构体)
debug_value %0 : $HotpotCat, let, name "a", argno 1 // id: %3
debug_value %1 : $HotpotCat, let, name "b", argno 2 // id: %4
debug_value %2 : $@thin HotpotCat.Type, let, name "self", argno 3 // id: %5
//结构体中取出age(Int)
%6 = struct_extract %0 : $HotpotCat, #HotpotCat.age // user: %8
%7 = struct_extract %1 : $HotpotCat, #HotpotCat.age // user: %9
//Int结构体中取出value
%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
//相等bb1,否则bb4
cond_br %10, bb1, bb4 // id: %11
bb1: // Preds: bb0
%12 = metatype $@thin String.Type // user: %18
//HotpotCat结构体中取出name
%13 = struct_extract %0 : $HotpotCat, #HotpotCat.name // users: %20, %18, %14
retain_value %13 : $String // id: %14
%15 = struct_extract %1 : $HotpotCat, #HotpotCat.name // users: %19, %18, %16
retain_value %15 : $String // id: %16
// function_ref static String.== infix(_:_:)
//String自己的==方法
%17 = function_ref @static Swift.String.== infix(Swift.String, Swift.String) -> Swift.Bool : $@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
//构造bool值-1
%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
//构造bool值0
%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
//构造bool值0
%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
//返回上面构造的bool值
return %32 : $Bool // id: %33
} // end sil function 'static main.HotpotCat.__derived_struct_equals(main.HotpotCat, main.HotpotCat) -> Swift.Bool'
- 1.取两个结构体
a
、b
- 2.比较
a.age
与b.age
- 3.取
a.name
与b.name
,并且调用String
自己的判等方法 - 4.构造
Int1
类型的数据,相等-1
,不相等0
。
底层通过Int1
类型的数据生成Bool
类型:
%29 = integer_literal $Builtin.Int1, 0 // user: %30
%30 = struct $Bool (%29 : $Builtin.Int1) // user: %31
所以也可以简单说Swift
中的Bool
是-1(true)
和0(false)
。
Bool.Swift
源码:
上面我们分析的是结构体,那么如果HotpotCat
是class
呢?
可以看到这个时候需要我们自己实现
==
方法了。
class HotpotCat {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
}
extension HotpotCat: Equatable {
static func == (lhs: HotpotCat, rhs: HotpotCat) -> Bool {
return lhs.age == rhs.age && lhs.name == rhs.name
}
}
var hp = HotpotCat.init(age: 18, name: "hotpot")
var hp1 = HotpotCat.init(age: 18, name: "hotpot")
var hp2 = hp
//比较值相等
print(hp == hp1)
//比较地址
print(hp === hp1)
print(hp === hp2)
true
false
true
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
}
Comparable
自动实现了Equatable
struct HotpotCat {
var age: Int
var name: String
}
extension HotpotCat: Comparable {
static func < (lhs: HotpotCat, rhs: HotpotCat) -> Bool {
return lhs.age < rhs.age && lhs.name < lhs.name
}
}
var hp = HotpotCat(age: 21, name: "hotpot1")
var hp1 = HotpotCat(age: 20, name: "hotpot")
print(hp > hp1)
在这里结构体必须实现<
,编译器会通过<
自动实现其它运算符。当然实现>
,不实现<
不行,由于源码中其它运算法是通过<
来实现的:
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 {
@inlinable
public static func > (lhs: Self, rhs: Self) -> Bool {
return rhs < lhs
}
@inlinable
public static func <= (lhs: Self, rhs: Self) -> Bool {
return !(rhs < lhs)
}
@inlinable
public static func >= (lhs: Self, rhs: Self) -> Bool {
return !(lhs < rhs)
}
}
??
??
运算符在optional
源码中有两个:
@_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()
}
}
@_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
,一个返回T?
,这里为什么有两个?看个例子就明白了
var age: Int? = nil
var age2: Int? = 10
var temp = age ?? age2
print(temp)
可以看到??
的返回值是和age2
相关的。age2
是什么类型就返回什么类型,age2
是可选类型就返回T?
,否则返回T
。
可选链
可选链是一个调用和查询可选属性、方法和下标的过程,它可能为 nil 。如果可选项包含值,属性、方法或者下标的调用成功;如果可选项是 nil ,属性、方法或者下标的调用会返回 nil 。多个查询可以链接在一起,如果链中任何一个节点是 nil ,那么整个链就会得体地失败。
class Hotpot {
var name: String?
var cat: Cat?
func test() {
print("test")
}
}
class Cat {
var catName: String?
func ctTest() {
print("ctTest")
}
}
var cat: Cat? = nil
var hp: Hotpot? = Hotpot()
let temp = hp?.cat?.catName
hp?.test()
hp?.cat?.ctTest()
test
unsafelyUnwrapped
在optional
源码中还有一个unsafelyUnwrapped
,实现如下:
/// The wrapped value of this instance, unwrapped without checking whether
/// the instance is `nil`.
///
/// The `unsafelyUnwrapped` property provides the same value as the forced
/// unwrap operator (postfix `!`). However, in optimized builds (`-O`), no
/// check is performed to ensure that the current instance actually has a
/// value. Accessing this property in the case of a `nil` value is a serious
/// programming error and could lead to undefined behavior or a runtime
/// error.
///
/// In debug builds (`-Onone`), the `unsafelyUnwrapped` property has the same
/// behavior as using the postfix `!` operator and triggers a runtime error
/// if the instance is `nil`.
///
/// The `unsafelyUnwrapped` property is recommended over calling the
/// `unsafeBitCast(_:)` function because the property is more restrictive
/// and because accessing the property still performs checking in debug
/// builds.
///
/// - Warning: This property trades safety for performance. Use
/// `unsafelyUnwrapped` only when you are confident that this instance
/// will never be equal to `nil` and only after you've tried using the
/// postfix `!` operator.
@inlinable
public var unsafelyUnwrapped: Wrapped {
@inline(__always)
get {
if let x = self {
return x
}
_debugPreconditionFailure("unsafelyUnwrapped of nil optional")
}
}
unsafelyUnwrapped
和!
强制解包一样。
var age: Int? = 20
print(age.unsafelyUnwrapped)
区别在于:
对于第2点,在
release -Onone
模式下验证下:image.png
可以看到
age.unsafelyUnwrapped
为0
。
as as? as!
var age: Int = 10
var age1 = age as Any
print(age1)
var age2 = age as? Double
print(age2)
var age3 = age as! Double
print(age3)
10
nil
SwiftClouse was compiled with optimization - stepping may behave oddly; variables may not be available.
image.png
访问控制
在OC
中很少用到访问控制,Swift
中主要针对其它源文件和模块对代码的访问控制。
Swift
为代码的实体提供个五个不同的访问级别。这些访问级别和定义实体的源文件相关,并且也和源文件所属的模块相关。如果不指明访问级别的话,代码中的所有实体都会默认为 internal 级别。大多数情况下不需要明确指定访问级别。访问控制权限
private
将实体的使用限制于封闭声明中。当一些细节仅在单独的声明中使用时,使用 private 访问隐藏特定功能的实现细节。
仅在声明的作用域有效
image.png
fileprivate
将实体的使用限制于当前定义源文件中。当一些细节在整个文件中使用时,使用 file-private 访问隐藏特定功能的实现细节。
仅限制在当前定义的源文件中
这个时候我们在另外一个文件新建一个SubHotpotCat
:
class SubHotpotCat: HotpotCat {
fileprivate var name:String = "subHotpotCat"
}
fileprivate func test() {
let subHotpot = SubHotpotCat()
print(subHotpot.name)
}
在HotpotCat
文件中访问subHotpot.name
:
internal
允许实体被定义模块中的任意源文件访问,但不能被该模块之外的任何源文件访问。通常在定义应用程序或是框架的内部结构时使用。
默认的访问级别,允许定义模块中的任意源文件访问,但不能被该模块之外的任何源文件访问。
这里的模块是指:一个框架或者应用程序。主要指import
关键字导入的模块
import Foundation
上面的Foundation
就是模块。三方库也是模块。
public
允许实体被定义模块中的任意源文件访问,同样可以被另一模块的源文件通过导入该定义模块来访问。在指定框架的公共接口时,通常使用 open 或 public 访问。
开放式访问,能够在其定义模块的任何源文件中使用代码,并且可以从另外一个源文件访问源文件。
只能在定义的模块中继承和子类重写。
open
最不受限制的访问级别
open 访问仅适用于类和类成员,它与 public 访问区别如下:
- public 访问,或任何更严格的访问级别的类,只能在其定义模块中被继承。
- public 访问,或任何更严格访问级别的类成员,只能被其定义模块的子类重写。
- open 类可以在其定义模块中被继承,也可在任何导入定义模块的其他模块中被继承。
- open 类成员可以被其定义模块的子类重写,也可以被导入其定义模块的任何模块重写。
显式地标记类为 open 意味着你考虑过其他模块使用该类作为父类对代码的影响,并且相应地设计了类的代码。
网友评论