Swift-枚举、结构体、类

Swift-枚举、结构体、类,第1张

概述Swift-枚举结构体、类 学习如下教程的记录 Getting to Know Enums, Structs and Classes in Swift Swift Tutorial: Initialization In Depth, Part 1/2 Swift中的type system: 枚举 参考: 官方文档-枚举 明确指定后备存储(backing store)类型的枚举被称为RawRepr Swift-枚举、结构体、类

学习如下教程的记录

Getting to Know Enums,Structs and Classes in Swift Swift Tutorial: Initialization In Depth,Part 1/2

Swift中的type system:

枚举

参考:

官方文档-枚举

明确指定后备存储(backing store)类型的枚举被称为RawRepresentable,因为它们自动采用RawRepresentable协议。

如下定义一个颜色枚举colorname:

enum colorname: String {    case black    case silver    case gray    case white    case maroon    case red}

当使用字符串作为枚举类型的原始值时,每个枚举成员的隐式原始值为该枚举成员的名称。

所以let black = colorname.black.rawValueblack

关联值

颜色处了有使用名字表示外,还可以使用RGB或者HSL来表示
如下定义CSScolor

enum CSScolor {  case named(colorname)  case rgb(UInt8,UInt8,UInt8)}

如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法接收一个叫做rawValue的参数,参数类型即为原始值类型,返回值则是枚举成员或nil

枚举中的方法和协议

在swift中,枚举与其它named type一样,可以采用协议
如果想输出CSScolor对象,可以采用customstringconvertible协议

extension CSScolor: customstringconvertible{    var description: String {        switch self {        case .named(let colorname):            return colorname.rawValue        case .rgb(let red,let green,let blue):            return String(format: "#%02X%02X%02X",red,green,blue)        }    }}

枚举的初始化方法
枚举也可以添加自定义的初始化方法

extension CSScolor { init(gray: UInt8) { self = .rgb(gray,gray,gray) } }
枚举类型的可失败构造器

如果提供的参数无法匹配任何枚举成员,则构造失败。

enum TemperatureUnit {    case Kelvin,Celsius,Fahrenheit    init?(symbol: Character) {        switch symbol {        case "K":            self = .Kelvin        case "C":            self = .Celsius        case "F":            self = .Fahrenheit        default:            return nil        }    }}
带原始值的枚举类型的可失败构造器

带原始值的枚举类型会自带一个可失败构造器init?(rawValue:),该可失败构造器有一个名为rawValue的参数

enum TemperatureUnit: Character {    case Kelvin = "K",Celsius = "C",Fahrenheit = "F"}let fahrenheitUnit = TemperatureUnit(rawValue: "F")if fahrenheitUnit != nil {    print("This is a defined temperature unit,so initialization succeeded.")}// 打印 "This is a defined temperature unit,so initialization succeeded."let unkNownUnit = TemperatureUnit(rawValue: "X")if unkNownUnit == nil {    print("This is not a defined temperature unit,so initialization Failed.")}// 打印 "This is not a defined temperature unit,so initialization Failed."
结构体

在枚举的extension中不能添加新的case,但结构体和类是可以的

Swift Standard library团队建议当创建一个新的model的时候,先使用protocol创建一个interface。在这里图形要可绘制,所以定义如下的protocol:

protocol Drawable {  func draw(with context: DrawingContext)}

DrawingContext也是一个protocolDrawingContext知道如何绘制纯几何类型:CircleRectangle和其他类型。实际的绘制方法要自己实现

protocol DrawingContext {  func draw(circle: Circle)  // more primitives will go here ...}

如下,定义一个结构体Circle采用Drawable协议:

struct Circle : Drawable {    var strokeWIDth = 5    var strokecolor = CSScolor.named(.red)    var fillcolor = CSScolor.named(.yellow)    var center = (x: 80.0,y: 160.0)    var radius = 60.0    // 实现Drawable协议    func draw(context: DrawingContext) {        context.draw(self)    }}

结构体与类类似。结构体与类的区别是,结构体是值类型,类是引用类型

与类不同,结构体的方法不允许修改存储属性,如果要修改的话使用mutating声明

mutating func shift(x: Double,y: Double) {  center.x += x  center.y += y}
结构体构造过程 默认构造器

如果没有存储属性,或者存储属性都有默认值时,可直接使用默认的构造器。同样,如果有可选类型的属性的变量,由于可选类型的存储属性默认初始化为nil,所以也看直接使用默认的构造器

struct RocketConfiguration {    let name: String = "Athena 9 Heavy"    let numberOfFirstStageCores: Int = 3    let numberOfSecondStageCores: Int = 1    var numberOfStageReuseLandingLegs: Int?}let athena9Heavy = RocketConfiguration()

但如果把var numberOfStageReuseLandingLegs: Int?,改为常量let

let numberOfStageReuseLandingLegs: Int?

这时let athena9Heavy = RocketConfiguration()就会编译报错

结构体的逐一成员构造器

处理默认构造器,如果结构体没有提供自定义的构造器,它们将自动获得一个逐一成员构造器,即使结构体的存储型属性没有默认值。

逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。

struct RocketStageConfiguration {    let propellantMass: Double    let liquIDOxygenMass: Double    let nominalBurnTime: Int}let stageOneConfiguration = RocketStageConfiguration(propellantMass: 119.1,liquIDOxygenMass: 276.0,nominalBurnTime: 180)

要注意的是:

如果调整存储属性的顺序,上面的构造方法也会报错,因为逐一构造器的方法的参数的顺序与存储属性的顺序是一致的 如果存储属性有默认值,如let nominalBurnTime: Int = 180,上面的构造方法也会报错。这是因为逐一构造器的参数只针对没有默认值的存储顺序

如果添加了自定义的构造方法,原来自动生成的逐一构造器方法就会无效

struct RocketStageConfiguration {    let propellantMass: Double    let liquIDOxygenMass: Double    let nominalBurnTime: Int    init(propellantMass: Double,liquIDOxygenMass: Double) {        self.propellantMass = propellantMass        self.liquIDOxygenMass = liquIDOxygenMass        self.nominalBurnTime = 180    }}let stageOneConfiguration = RocketStageConfiguration(propellantMass: 119.1,nominalBurnTime: 180)//报错

但如果任然需要自动生成的逐一构造器方法,该怎么办呢?答案是使用extension,即把自定义的初始化方法放在extension

struct RocketStageConfiguration {  let propellantMass: Double  let liquIDOxygenMass: Double  let nominalBurnTime: Int}extension RocketStageConfiguration {  init(propellantMass: Double,liquIDOxygenMass: Double) {    self.propellantMass = propellantMass    self.liquIDOxygenMass = liquIDOxygenMass    self.nominalBurnTime = 180  }}
自定义构造方法

一个初始化方法必须给每个不带默认值的存储属性指定值,否则的话报错
可以给构造方法的参数指定默认值

struct Weather {    let temperatureCelsius: Double    let windSpeedKilometersPerHour: Double    init(temperatureFahrenheit: Double = 72,windSpeedMilesPerHour: Double = 5) {        self.temperatureCelsius = (temperatureFahrenheit - 32) / 1.8        self.windSpeedKilometersPerHour = windSpeedMilesPerHour * 1.609344    }}
值类型的构造器代理

构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。

要注意的是,构造器代理中不能实例化任何属性,原因是你调用的改造方法,可能会修改你的属性,这样是不安全的,如下会报错

init(zAngularVeLocitydegreesPerMinute: Double) {  self.needsCorrection = false//报错  self.init(zAngularVeLocitydegreesPerMinute: zAngularVeLocitydegreesPerMinute,needsCorrection: self.needsCorrection)}
Two-Phase Initialization(两段式构造过程)

如下:

struct CombustionChAmberStatus {  var temperatureKelvin: Double  var pressureKiloPascals: Double  init(temperatureKelvin: Double,pressureKiloPascals: Double) {    print("Phase 1 init")    self.temperatureKelvin = temperatureKelvin    self.pressureKiloPascals = pressureKiloPascals    print("CombustionChAmberStatus fully initialized")    print("Phase 2 init")  }  init(temperatureCelsius: Double,pressureAtmospheric: Double) {    print("Phase 1 delegating init")    let temperatureKelvin = temperatureCelsius + 273.15    let pressureKiloPascals = pressureAtmospheric * 101.325    self.init(temperatureKelvin: temperatureKelvin,pressureKiloPascals: pressureKiloPascals)    print("Phase 2 delegating init")  }}CombustionChAmberStatus(temperatureCelsius: 32,pressureAtmospheric: 0.96)

调试区域输出如下:

Phase 1 delegating initPhase 1 initCombustionChAmberStatus fully initializedPhase 2 initPhase 2 delegating init

可见Phase 1 delegating initself.init(.....)之间,不可以使用self

可失败构造器

你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在init关键字后面添加问号(init?)

注意:你只是用return nil表明可失败构造器构造失败,而不要用关键字return来表明构造成功。

struct Animal {    let specIEs: String    init?(specIEs: String) {        if specIEs.isEmpty { return nil }        self.specIEs = specIEs    }}

除了返回nil,还可以抛出异常,如下:

struct Astronaut {    let name: String    let age: Int    init(name: String,age: Int) throws {        if name.isEmpty {            throw InvalIDAstronautDataError.Emptyname        }        if age < 18 || age > 70 {            throw InvalIDAstronautDataError.InvalIDAge        }        self.name = name        self.age = age    }}enum InvalIDAstronautDataError: Error {    case Emptyname    case InvalIDAge}

在调用可抛出异常的构造器方法时,使用try关键字,或者也可以使用try?或者try!

try、try?、try!的区别?
参考try,try! & try? what’s the difference,and when to use each?

try?会试图执行一个可能会抛出异常的 *** 作。如果成功抛出异常,执行的结果就会包裹在可选值(optional)里;如果抛出异常失败(比如:已经在处理 error),那么执行的结果就是nil,而且没有 error。try?配合if let和guard一起使用效果更佳

let johnny = try? Astronaut(name: "Johnny Cosmoseed",age: 17) // nil
类 属性 计算属性
extension Circle {  var diameter: Double {    get {      return radius * 2    }    set {      radius = newValue / 2    }  }}

更多的时候,需要的是一个getter方法:

var area: Double {  return radius * radius * Double.pi}
属性观察器

属性观察器可以用来限制值或者格式
willSet在赋值之前处理一些逻辑,使用newValue获取新值
当调用dIDSet的时候,属性的值已经变成了新值,要想获取原值可以使用oldValue

var current = 0 {        // 可以不声明新的变量名,使用newValue        willSet(newCurrent){            // 此时,current还是以前的值            print("Current value changed. The change is \(abs(current-newCurrent))")        }        // property observer可以用来限制值或者格式        // 也可以用来做关联逻辑        // 可以不声明新的变量名,使用oldValue获取原来的值        dIDSet(oldCurrent){            // 此时,current已经是新的值            if current == lightBulb.maxCurrent{                print("Pay attention,the current value get to the maximum point.")            }            else if current > lightBulb.maxCurrent{                print("Current too high,falling back to prevIoUs setting.")                current = oldCurrent            }            print("The current is \(current)")        }    }

注意:dIDSet和willSet不会在初始化阶段调用
因此,dIDSetwillSet对常量let没有意义,因为let只在初始化阶段赋值

如下的定义:

enum theme{    case DayMode    case Nightmode}class UI{    var Fontcolor: UIcolor!    var backgroundcolor: UIcolor!    var themeMode: theme = .DayMode{        dIDSet{            switch(themeMode){            case .DayMode:                Fontcolor = UIcolor.blackcolor()                backgroundcolor = UIcolor.whitecolor()            case .Nightmode:                Fontcolor = UIcolor.whitecolor()                backgroundcolor = UIcolor.blackcolor()            }        }    }    init(){        self.themeMode = .DayMode    }}

进行如下的初始化,会发现Fontcolorbackgroundcolornil

let ui = UI()ui.themeModeui.Fontcolor//为nilui.backgroundcolor//为nil
便利构造函数和指定构造函数

要注意的点:

1.构造函数参数可以有默认值
2.构造函数可以重载

两种构造器:

指定构造器-指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。

```init(parameters) {    statements}```

便利构造器-便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值

```convenIEnce init(parameters) {    statements}```

注意:

1.只有指定构造函数可以调用super.init(),便利构造函数不能调用super.init()
2.便利构造函数最终一定要调用一个指定的构造函数

可失败构造器

一般来说,要让指定构造器不可失败,而让便利构造器可失败,如下的RocketComponent

class RocketComponent {    let model: String    let serialNumber: String    let reusable: Bool    // Init #1a - Designated    init(model: String,serialNumber: String,reusable: Bool) {        self.model = model        self.serialNumber = serialNumber        self.reusable = reusable    }    // Init #1b - ConvenIEnce    convenIEnce init(model: String,serialNumber: String) {        self.init(model: model,serialNumber: serialNumber,reusable: false)    }    // Init #1c - Designated    convenIEnce init?(IDentifIEr: String,reusable: Bool) {        let IDentifIErComponents = IDentifIEr.components(separatedBy: "-")        guard IDentifIErComponents.count == 2 else {            return nil        }        self.init(model: IDentifIErComponents[0],serialNumber: IDentifIErComponents[1],reusable: reusable) } }
类的两段式构造

类的两段式构造需要注意的是:

1.关于父类的属性必须通过父类的构造函数来进行构造
2.在swifi语言中,必须将子类相关的所有量进行初始化之后,才能调用super.init()
3.在swift中整个构造函数可以分为2部分,顺序不能混淆

构造初值,在类没有构造完成的时候,不能使用self。但是静态的方法和静态量在第一阶段是可以使用的

convenIEnce init(group: String = ""){        let name = User.generateUsername()        self.init(name:name,group: group)    }    static func generateUsername() -> String{        return "Player" + String(rand()%1_000_000)    }
进一步完成类相关属性的值

如下:

init(name: String,group: String){ // Phase1: 从子类向父类初始化所有的变量 self.group = group super.init(name: name) // Phase2: 所有成员变量初始化完成以后,进行成员变量相关的逻辑调整 if self.group == ""{ getscore(-10) self.life -= 5 } }
继承

final表示类不可继承

构造函数的继承

如果子类实现了父类所有的指定构造函数,则自动继承父类的所有便利构造函数

required关键字
required表示必须被子类所实现的构造函数

//父类    required init(name: String){        self.name = name    }    //子类    convenIEnce required init(name: String){        self.init(name: name,group: "")    }

注意:
1.required关键字标注的的构造函数,不需要写overrIDe关键字

总结

以上是内存溢出为你收集整理的Swift-枚举、结构体、类全部内容,希望文章能够帮你解决Swift-枚举、结构体、类所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1060067.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存