Swift学习(五)枚举 Enum 和可选项Optional

1,861 阅读4分钟

枚举

枚举值是我们开发过程中最常见的类型之一,在OC中枚举值的实际类型是NSInterge。而在Swift中,枚举值赋予了更多,更灵活的用法,了解了枚举的用法,可以让我们代码更简洁,逻辑更清晰。 swfit中使用关键子enum来声明一个枚举类型。

enum OSCardType {

    case OSCardTypeOne

    case OSCardTypeTwo

    case OSCardTypeThree

}

Swift 中的枚举非常灵活,并且不需给枚举中的每一个成员都提供值。如果一个值(所谓“原 始”值)要被提供给每一个枚举成员,那么这个值可以是字符串、字符、任意的整数值,或者是 浮点类型。

enum OSDeviceCode: String {

    case OSDeviceCodeLight = "light"

    case OSDeviceCodeAirConditioner = "airConditioner"

}

RawValue 也可以通过编译器去推断, 这是建立在 Swift 的类型推断机制上的

enum OSDayOfWeek: Int {

    case mon, tue, wed, thu, fri = 10, sat, sun

}

mon = 0, ture = 1。。。。sat = 11, sun = 12。 如果是String类型,rawValue默认就是枚举的字符串。

关联值

除了以上用法,Swift中枚举值还能携带更多信息,从而来表达更复杂的案例。可以跟随若干个关联值。

enum OSShape {
    case circle(redious: Double)
    case ractange(width: Double, height: Double)
}

在模式比配的时候要注意,Swift枚举值的case需要匹配所有枚举类型,否则编译器将会报错。带有关联值的模式匹配时候,关联值以参数的方式出现。

func func1() {
        
        let cricle = OSShape.circle(redious: 10.0)
        
        switch cricle {
        case .circle(let circel):
            print(circel)
        case .ractange(let width, let height):
            print(width, height)
        }
    }

枚举占内存的大小

枚举是值类型,是存储在栈上面的。存储类型分为两种,No-payload enumsingle-payload enum

可以通过打印来查看枚举的大小。

func func2() {

        print(MemoryLayout<OSCardType>.size)

        

        var card1 = OSCardType.OSCardTypeOne

        var card2 = OSCardType.OSCardTypeTwo

        print(card1,card2)

    }

WX20220109-190038@2x.png

我们继续看看带关联值的内存大小。

enum SingleEnum1 {
        case One(isgood: Bool)
        case Two
        case Three
    }
    
    enum SingleEnum2 {
        case One(age: Int)
        case Two
        case Three
    }
    
    func func3() {
        print(MemoryLayout<SingleEnum1>.size)
        print(MemoryLayout<SingleEnum2>.size)
    }

输出1、9

这里需要注意:SingleEnum1是系统做了内存优化,节省了内存开支,Bool类型实际只用了1bite存储值,剩下的位用来存放枚举信息。网上有很多资料显示枚举的大小等于,最大的关联类型所占内存+枚举值内存,这里Bool类型是一个例外。非常容易忽略。在源码中也可以找到相应的描述。

image.png 大概翻译一下:如果储存空间够存放关联值,那么负载大小和枚举值大小一样。

indeirect关键字

image.png 分析:值类型在编译时期就已经确定了大小,而上面这种“套娃”方式,无法确定内存大小。我们使用编译器的修改方法。

indirect enum BinaryTree<T> {
        case empty
        case node(left: BinaryTree, right: BinaryTree, value: T)
    }
func func4() {
        var node = BinaryTree<Int>.node(left: BinaryTree<Int>.empty, right: BinaryTree<Int>.empty, value: 1)
        print(node)
        print(MemoryLayout<BinaryTree<Int>>.size)
        print(MemoryLayout<BinaryTree<Int>>.stride)
    }

输出位 8 8,继续使用lldb打印地址看看。

image.png 这时候发现,当使用indirect关键字修饰后,系统会在堆区给该枚举分配空间。

可选值

可选值在我们swift编程的过程中随处可见,主要是为了安全性考虑设计出来的。可选值其实也是通过枚举来实现的。

class InFuncRoom {
          var roomId: String?
       }

也可以这样写:

class InFuncRoom {
//            var roomId: String?
            var roomId: Optional<String> = nil
        }

点击Optional看实现 image.png 显然这个可选值就是一个枚举。

通过一个实例,来使用一下类似这种处理,从而体验可选值的使用。

题目:从数组中剔除所有偶数。

//自己实现可选值
    func func6() {
        var array = [1,2,3,4,5,6]
        for value in array {
            let optional = getOddValue(value)
            switch optional {
            case .some(let ele):
                array.remove(at: array.firstIndex(of: ele)!)
                print(ele)
            default:
                print("not exit")
            }
        }
        
        print(array);
    }
    
    func getOddValue(_ value: Int) -> MyOptional<Int> {
        if value % 2 == 0 {
            return .some(value)
        } else {
            return .none
        }
    }

解包

如果我们每一个可选值都用模式匹配的方式(switch case)来获取值,代码书写上回比较繁琐,我们可以使用 if let 的方式来进行可选值绑定(解包)。

    func func7() {
        var name: String? = "hahaha"
        if let name = name {
            print(name)
        }
        name = nil
        if let name = name {
            print(name)
        }  
    }

除了使用 if let 来处理可选值之外,我们还可以使用 gurad let 来简化我们的代码,我们 来看一个具体的案例。

guard let if let 刚好相反, guard let 守护一定有值。如果没有,直接返回。 通常判断是否有值之后,会做具体的逻辑实现,通常代码多 如果用 if let 凭空多了一层分支,guard let是降低分支层次的办法

运算符重载

struct Vector {
  let x: Int
  let y: Int
}

extension Vector {
  static func + (fistVector: Vector, secondVector: Vector) -> Vector {
    return Vector(x:  fistVector.x + secondVector.x, y: fistVector.y + secondVector.y)
  }

  static prefix func - (vector: Vector) -> Vector {
    return Vector(x: -vector.x, y: -vector.y)
  }

  static func - (fistVector: Vector, secondVector: Vector) -> Vector {
    return fistVector + -secondVector
  }

}
func func6() {
        let x = Vector(x: 10, y: 20)
        let y = Vector(x: 80, y: 50)
        let z = x + y
        print(z)
    }