Swift5复习(二)可选项、结构体、类、闭包

2020-05-25  本文已影响0人  默默_David

Swift复习(二)可选项、结构体、类、闭包

一、 可选项(Optional)

定义

强制解包(Forced Unwrapping)

var a : String?
a!//error: Fatal error: Unexpectedly found nil while unwrapping an Optional value

可选值绑定(Optional Binding)

if let number = Int("123"){
    print("字符串转换整形成功:\(number)") //字符串转换整形成功:123
} else {
    print("字符串转化整形失败")
}

enum Season : Int{
    case Spring = 1, summer,autumn,winter
}
var season : Season? = .summer
/*
 这里可以用var来接收,表示可修改
 可以使用同名变量接收
 */
if var season = season {
    season = .winter
    print(season.rawValue)//4
}

多个可选绑定

if let first = Int("4"),let second = Int("5") , first < second,second < 10{
    print("成功了,first:\(first),second:\(second)")//成功了,first:4,second:5
}

空合运算符??(Nil-Coalescing Operator)

public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T

guard语句

guard 条件 else {
    //do something
    退出当前作用域
    //return、break、continue、throw error
}

隐式解包(Implicitly Unwrapped Optional)

let num : Int! = 10
print(num + 1)//11

可选项字符串插值

var age : Int? = 18
print("My age is \(age)")//String interpolation produces a debug description for an optional value; did you mean to make this explicit?
print("My age is \(age!)")//My age is 18
print("My age is \(String(describing: age))")//My age is Optional(18)
print("My age is \(age ?? 0)")//My age is 18

可选项的本质是枚举

let a: Optional<Int> = 1

如上代码所示,我们的点进源码的定义处,可以看到:

@frozen public enum Optional<Wrapped> : ExpressibleByNilLiteral {

/// 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)
}

所以,可选项的本质是枚举,它有两个case,分别是none和some,如果可选值为nil,它是none,如果有值,它是some。

可选嵌套

有如下代码

let a: Int? = 1
let b: Int?? = a
let c: Int??? = b

我们使用fr v -R查看其结构

(lldb) fr v -R a
(Swift.Optional<Swift.Int>) a = some {
  some = {
    _value = 1
  }
}
(lldb) fr v -R b
(Swift.Optional<Swift.Optional<Swift.Int>>) b = some {
  some = some {
    some = {
      _value = 1
    }
  }
}
(lldb) fr v -R c
(Swift.Optional<Swift.Optional<Swift.Int?>>) c = some {
  some = some {
    some = some {
      some = {
        _value = 1
      }
    }
  }
}

可以看出,它是一层层的可选值封装,就像下面的二叉树结构:


从上图中我们可以看到,none可以出现在任意一层,那么在每一层的效果一样吗?
我们看如下代码:

let a: Int? = nil
let b: Int?? = a
let c: Int??? = b
let d: Int??? = nil

同样查看内存结构

(lldb) fr v -R a
(Swift.Optional<Swift.Int>) a = none {
  some = {
    _value = 0
  }
}
(lldb) fr v -R b
(Swift.Optional<Swift.Optional<Swift.Int>>) b = some {
  some = none {
    some = {
      _value = 0
    }
  }
}
(lldb) fr v -R c
(Swift.Optional<Swift.Optional<Swift.Int?>>) c = some {
  some = some {
    some = none {
      some = {
        _value = 0
      }
    }
  }
}
(lldb) fr v -R d
(Swift.Optional<Swift.Optional<Swift.Int?>>) d = none {
  some = some {
    some = some {
      some = {
        _value = 0
      }
    }
  }
}

我们看到,b和c都是some,而d是none,拿c来说,c是一个Optional.some(Optional.some(Optional.none)),而d因为是直接赋值为nil,所以它是一个Optional.none.

假如这个时候我们进行可选绑定

let a: Int? = nil
let b: Int?? = a
let c: Int??? = b
let d: Int??? = nil

if let _ = a{
    print("a不为空")
}
if let _ = b{
    print("b不为空")
}
if let _ = c{
    print("c不为空")
}
if let _ = d{
    print("a不为空")
}
//打印结果
b不为空
c不为空

我们再看这个例子

let a: Int? = nil
let b: Int?? = a
let c: Int??? = b
let d: Int??? = nil

if a == b{
    print("a和b相等")
}
if b == c{
    print("b和c相等")
}
if a == c{
    print("a和c相等")
}
if c == d {
    print("c和d相等")
}
if a == d{
    print("a和d相等")
}
//打印结果
a和b相等
b和c相等
a和c相等

为什么结果是这样呢,我们查看关于可选值的==的定义

extension Optional : Equatable where Wrapped : Equatable {

    /// Returns a Boolean value indicating whether two optional instances are
    /// equal.
    ///
    /// Use this equal-to operator (`==`) to compare any two optional instances of
    /// a type that conforms to the `Equatable` protocol. The comparison returns
    /// `true` if both arguments are `nil` or if the two arguments wrap values
    /// that are equal. Conversely, the comparison returns `false` if only one of
    /// the arguments is `nil` or if the two arguments wrap values that are not
    /// equal.
    ///
    ///     let group1 = [1, 2, 3, 4, 5]
    ///     let group2 = [1, 3, 5, 7, 9]
    ///     if group1.first == group2.first {
    ///         print("The two groups start the same.")
    ///     }
    ///     // Prints "The two groups start the same."
    ///
    /// You can also use this operator to compare a non-optional value to an
    /// optional that wraps the same type. The non-optional value is wrapped as an
    /// optional before the comparison is made. In the following example, the
    /// `numberToMatch` constant is wrapped as an optional before comparing to the
    /// optional `numberFromString`:
    ///
    ///     let numberToFind: Int = 23
    ///     let numberFromString: Int? = Int("23")      // Optional(23)
    ///     if numberToFind == numberFromString {
    ///         print("It's a match!")
    ///     }
    ///     // Prints "It's a match!"
    ///
    /// An instance that is expressed as a literal can also be used with this
    /// operator. In the next example, an integer literal is compared with the
    /// optional integer `numberFromString`. The literal `23` is inferred as an
    /// `Int` instance and then wrapped as an optional before the comparison is
    /// performed.
    ///
    ///     if 23 == numberFromString {
    ///         print("It's a match!")
    ///     }
    ///     // Prints "It's a match!"
    ///
    /// - Parameters:
    ///   - lhs: An optional value to compare.
    ///   - rhs: Another optional value to compare.
    @inlinable public static func == (lhs: Wrapped?, rhs: Wrapped?) -> Bool
}

再看一段代码

let a: Int? = 1
let b: Int?? = a
let c: Int??? = b
let d: Int??? = 1

if a == b{
    print("a和b相等")
}
if b == c{
    print("b和c相等")
}
if a == c{
    print("a和c相等")
}
if c == d {
    print("c和d相等")
}
if a == d{
    print("a和d相等")
}
//打印结果
a和b相等
b和c相等
a和c相等
c和d相等
a和d相等

==的注释中有这样一段话:

The comparison returns true if both arguments are nil or if the two arguments wrap values that are equal.

翻译过来就是,要返回true,要么是两个都是nil,要么是warp值相等。
按照我们上面的案例,a=1,d=1的时候,它们warp值都是1,所以相等。a=nil,d=nil的时候,a、b、c都是some,所以我们比较它里面的warp值,都是nil,所以相等,而d是none,所以d和所有其他的都不相等。
我们可以测试一下:

let a: Int? = nil
let b: Int?? = a
let c: Int??? = b
let d: Int??? = Optional<Int>.none

if a == b{
    print("a和b相等")
}
if b == c{
    print("b和c相等")
}
if a == c{
    print("a和c相等")
}
if c == d {
    print("c和d相等")
}
if a == d{
    print("a和d相等")
}
打印结果
a和b相等
b和c相等
a和c相等
c和d相等
a和d相等

结果也和我们的注释一样。

结构体

介绍

struct Date{
    var year: Int = 2020
    var month: Int
    var day: Int
}
var date = Date(year: 2020, month: 1, day: 20)
date = Date(month: 2, day: 22)

自定义初始化器

struct Date{
    var year: Int = 2020
    var month: Int
    var day: Int
    init(year: Int,month: Int,day: Int) {
        self.year = year
        self.month = month
        self.day = day
    }
}
var date = Date(year: 2020, month: 1, day: 20)
date = Date(month: 2, day: 22)//报错 Missing argument for parameter 'year' in call

定义

class Date{
    var year: Int = 2020
    var month: Int
    var day: Int
    init(year: Int,month: Int,day: Int) {
        self.year = year
        self.month = month
        self.day = day
    }
}

类的初始化器

class Point {
    var x: Int = 10
    var y: Int = 20
}
let p1 = Point()

class Point{
    var x: Int
    var y: Int
    init() {
        x = 10
        y = 20
    }
}
let p2 = Point()
//上面两段代码是完全等效的

结构体与类的本质区别

值类型

引用类型

嵌套类型

struct Poker{
    enum Suit : String {
        case spades,hearts,diamonds,clubs
    }
    enum Rank : Int {
        case tow = 2,three,four,five,six,seven,eight,nine,ten
        case jack,queen,king,ace
    }
}
print(Poker.Suit.hearts.rawValue)//hearts

var rank = Poker.Rank.five
rank = .king
print(rank.rawValue)//13

枚举、结构体、类都可以定义方法

方法占用对象的内存吗?
不占用
方法的本质就是函数
方法、函数都存放在代码段

闭包

闭包(Closures)是自包含的功能代码块,可以在代码中使用或者用来作为参数传值。
相比OC的block,Swift的闭包有很多优化的地方:

  1. 可以根据上下文推断参数和返回值类型
  2. 从单行表达式闭包中隐式返回(也就是闭包体只有一行代码,可以省略return)
  3. 可以使用简化参数名,如$0,$1(从0开始,表示第i个参数)
  4. 提供了尾随闭包语法(Trailing closure syntax)

闭包表达式(Closure Expression)

func sum(_ v1: Int,_ v2: Int) -> Int { v1 + v2 }

var fn = { (v1: Int,v2: Int) -> Int in
    v1 + v2
}
fn(10,20)//30

{
    (参数列表) -> 返回值类型 in
    函数体代码
}

闭包表达式的简写

func exec(v1: Int,v2: Int,fn: (Int,Int) -> Int){
    print(fn(v1,v2))
}
exec(v1: 10, v2: 20,fn: { (v1, v2) -> Int in
    v1 + v2
})
exec(v1: 10, v2: 20,fn : { v1, v2 in
    v1 + v2
})
exec(v1: 10, v2: 20,fn : {$0 + $1})
exec(v1: 10, v2: 20,fn : +)

尾随闭包

func exec(v1: Int,v2: Int,fn: (Int,Int) -> Int){
    print(fn(v1,v2))
}
exec(v1: 10, v2: 20) {
    $0 + $1
}
func exec(fn: (Int,Int) -> Int) {
    print(fn(1,2))
}
exec(fn: { $0 + $1 })
exec(){ $0 + $1 }
exec{ $0 + $1 }
func exec(fn: (Int,Int) -> Int) {
    print(fn(1,2))
}
exec{ _,_ in 10 }//10

函数原型
@inlinable public mutating func sort(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows

/*
 返回true:i1排在i2前面
 返回false:i1排在i2后面
 */
func compare(i1: Int,i2: Int) -> Bool{
    //大的排在前面
    return i1 > i2
}

array.sort(by: compare)
print(array) //[9, 8, 6, 4, 3, 1]

array.sort { (i1, i2) -> Bool in
    i1 < i2
}
print(array)//[1, 3, 4, 6, 8, 9]

//下方是逐步的简写
//因为已知返回为Bool,可以省略
array.sort { (i1, i2) in i1 < i2 }
//已知参数类型,用$0,$1分别指代第一个第二个参数
array.sort { $0 < $1 }
//其它已知,直接给一个判断条件
array.sort(by: <)

闭包的定义

一个函数和它所捕获的变量/常量环境组合起来,成为闭包

注意
如果返回值是函数类型,那么参数的修饰要保持统一

func add(_ num: Int) -> (inout Int) -> Void{
    func plus(v: inout Int){
        v += num
    }
    return plus
}

捕获值

闭包可以在其定义的上下文中捕获常量或变量
即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内应用和修改这些值

//案例一:
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}

let incrementByTen = makeIncrementor(forIncrement: 10)
// 返回的值为10
print(incrementByTen())
// 返回的值为20
print(incrementByTen())
// 返回的值为30
print(incrementByTen())

//案例二:
var num = 10
let ss = { () -> Int in
    num += 10
    return num
}
print(ss())//20
print(num)//20
print(ss())//30
print(num)//30
num = 100
print(ss())//110
print(num)//110
print(ss())//120
print(num)//120

逃逸闭包(@escaping)

当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 @escaping,用来指明这个闭包是允许“逃逸”出这个函数的。

举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。例如:

//因为回调在函数返回后才返回,所以必须加上@escaping标记
func download(_ completionHandler : @escaping ()->Void){
    DispatchQueue.global().async {
        Thread.sleep(forTimeInterval: 2)
        DispatchQueue.main.async {
            completionHandler()
        }
    }
}

自动闭包(@autoclosure)

自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。

我们经常会调用采用自动闭包的函数,但是很少去实现这样的函数。举个例子来说,assert(condition:message:file:line:) 函数接受自动闭包作为它的 condition 参数和 message 参数;它的 condition 参数仅会在 debug 模式下被求值,它的 message 参数仅当 condition 参数为 false 时被计算求值。

自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行。延迟求值对于那些有副作用(Side Effect)和高计算成本的代码来说是很有益处的,因为它使得你能控制代码的执行时机。

func returnSelfOrOther(_ num: Int,_ other: @autoclosure () -> Int) -> Int{
    num >= 0 ? num : other()
}

let result = returnSelfOrOther(-1) { 20 }
print(result) // 20

注意
过度使用 autoclosures 会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的

上一篇 下一篇

猜你喜欢

热点阅读