• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Swift从入门到精通第十一篇 - 初始化 初识

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

初始化(学习笔记)

环境Xcode 11.0 beta4 swift 5.1

  • 初始化
    • 初始化是类、结构体、枚举生成实例的过程,为该类的每个存储属性设置初始值,有些在实例使用前的设置或初始化也可在此实现;
    • Swift初始化函数不用写返回值,确保新类型的实例在使用前被正确初始化
    • 类类型也可以实现反初始化器,可以在实例销毁的时自定义清理操作
  • 为存储属性设置初始值
    • 类和结构体必须为所有存储属性设置一个合适的值
    • 可以在初始化或属性定义的时候设置值,此时设置的值不会触发属性观察器
    • 初始化器:创建一个指定类型的实例,用关键字 init

      init() {
          // perform some initialization here
      }
      // 定义一个结构体类
      struct Fahrenheit {
          var temperature: Double
          init() {
              temperature = 32.0
          }
      }
      var f = Fahrenheit()
      print("The default temperature is \(f.temperature)° Fahrenheit")
      // Prints "The default temperature is 32.0° Fahrenheit"
    • 默认属性初始值,属性声明时赋值

      struct Fahrenheit {
          var temperature = 32.0
      }
  • 自定义初始化
    • 初始化参数:可以在初始化定义时提供初始化参数,与函数和方法参数的功能一样

      struct Celsius {
          var temperatureInCelsius: Double
          init(fromFahrenheit fahrenheit: Double) {
              temperatureInCelsius = (fahrenheit - 32.0) / 1.8
          }
          init(fromKelvin kelvin: Double) {
              temperatureInCelsius = kelvin - 273.15
          }
      }
      let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
      // boilingPointOfWater.temperatureInCelsius is 100.0
      let freezingPointOfWater = Celsius(fromKelvin: 273.15)
      // freezingPointOfWater.temperatureInCelsius is 0.0
    • 参数名字和参数标签:参数名字函数体内使用,参数标签用在方法调用时使用

      struct Color {
          let red, green, blue: Double
          init(red: Double, green: Double, blue: Double) {
              self.red   = red
              self.green = green
              self.blue  = blue
          }
          init(white: Double) {
              red   = white
              green = white
              blue  = white
          }
      }
      let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
      let halfGray = Color(white: 0.5)
      let veryGreen = Color(0.0, 1.0, 0.0)
      // this reports a compile-time error - argument labels are required
    • 不带参数标签的初始化器,用 _ 代替

      struct Celsius {
          var temperatureInCelsius: Double
          init(fromFahrenheit fahrenheit: Double) {
              temperatureInCelsius = (fahrenheit - 32.0) / 1.8
          }
          init(fromKelvin kelvin: Double) {
              temperatureInCelsius = kelvin - 273.15
          }
          init(_ celsius: Double) {
              temperatureInCelsius = celsius
          }
      }
      let bodyTemperature = Celsius(37.0)
      // bodyTemperature.temperatureInCelsius is 37.0
    • 可选类型属性会自动初始化一个 nil 值,就算调用初始化器以后也有可能是没有值

      class SurveyQuestion {
          var text: String
          var response: String?
          init(text: String) {
              self.text = text
          }
          func ask() {
              print(text)
          }
      }
      let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
      cheeseQuestion.ask()
      // Prints "Do you like cheese?"
      cheeseQuestion.response = "Yes, I do like cheese."
    • 初始化过程中给常量属性赋值,与在定义常量时赋值效果一样且只能赋值一次,子类也不能修改

      class SurveyQuestion {
          let text: String
          var response: String?
          init(text: String) {
              self.text = text // 仅只能赋值一次
          }
          func ask() {
              print(text)
          }
      }
      let beetsQuestion = SurveyQuestion(text: "How about beets?")
      beetsQuestion.ask()
      // Prints "How about beets?"
      beetsQuestion.response = "I also like beets. (But not with cheese.)"
  • 默认初始化器
    • 类和结构体有一个默认的初始化器,为所有存储属性设置默认值

      class ShoppingListItem {
          var name: String?
          var quantity = 1
          var purchased = false
      }
      var item = ShoppingListItem() // 为三个属性设置默认值, 可选类型 nil 也算
    • 结构体类型成员初始化器,可以自动根据存储属性是在定义时有值生成多个初始化器

      struct Size {
          var width = 0.0, height = 0.0
      }
      let twoByTwo = Size(width: 2.0, height: 2.0)
      let zeroByTwo = Size(height: 2.0)
      print(zeroByTwo.width, zeroByTwo.height)
      // Prints "0.0 2.0"
      //
      let zeroByZero = Size()
      print(zeroByZero.width, zeroByZero.height)
      // Prints "0.0 0.0"
  • 值类型初始化器代理
    • 初始化器可以调用其它初始化器,俗称初始化器代理;值类型和 class 类型不同,值类型(结构体、枚举)不支持继承,简单调用就好;class类型要考虑所有的存储属性(包括继承的)要赋值一个合适的值;对于值类型用 self.init 可以调用其它初始化器;如果是值类型自定义初始化器则系统不会自动生成初始化器;如果想自定义不影响自动生成的初始化器,可以将自定义的写在 extension

      struct Size {
          var width = 0.0, height = 0.0
      }
      struct Point {
          var x = 0.0, y = 0.0
      }
      struct Rect {
          var origin = Point()
          var size = Size()
          init() {}
          init(origin: Point, size: Size) {
              self.origin = origin
              self.size = size
          }
          init(center: Point, size: Size) {
              let originX = center.x - (size.width / 2)
              let originY = center.y - (size.height / 2)
              self.init(origin: Point(x: originX, y: originY), size: size)
          }
      }
      let basicRect = Rect()
      // basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)
      let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
                            size: Size(width: 5.0, height: 5.0))
      // originRect's origin is (2.0, 2.0) and its size is (5.0, 5.0)
      let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
                            size: Size(width: 3.0, height: 3.0))
      // centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
  • 类的继承和初始化
    • 类的所有存储属性在初始化时都必须要有初始值, swift提供了两种初始化器:初始化器和便捷初始化器;每个类要有一个指定的初始化器,通常一个类只有一个
    • 指定初始化器和便捷初始化器语法

      init(#parameters#){
          #statements#
      }
      convenience init(#parameters#){
          #statements#
      }
    • class 类型初始化器代理,指定和便捷初始化器遵循以下三条准则:1. 指定初始化器必须调用直接父类的一个指定初始化器;2. 便捷初始化器必须调用同类的一个其它初始化器;3. 便捷初始化器最终要调用一个指定初始化器;见下图

  • 初始化两阶段:第一阶段:每一个存储属性赋一个初始值;第二阶段:可以进一步自定义存储属性相关操作;Swift编译器通过4个安全检查来确保两阶段初始化的完整性
    • 安全检查一、指定的初始化器必须确保在将其委托给父类初始化器之前初始化其类引入的所有属性;只有在知道对象所有存储属性的初始状态之后,才会认为对象的内存已经完全初始化。为了满足这个规则,指定的初始化器必须确保在传递到链之前初始化了它自己的所有属性。
    • 安全检查二、指定初始化器给继承属性赋值前必须先要委托到父类初始化器,如果不这样做赋的值会被父类覆盖
    • 安全检查三、便捷初始化器给任意一个属性赋值前必须要委托到一个初始化器,如果不这样做赋的值会被自己类的指定初始化器覆盖
    • 安全检查四、在初始化第一阶段完成前,初始化器不能调用任何实例方法、读取任何实例属性的值或者引用 self 做为一个值
  • 基于以上四项检查,以下是两阶段执行过程
    • 阶段一
      • 一个指定或便捷初始化器被调用
      • 分配类的新实例的内存,内存还没有初始化完成
      • 指定初始化器确保类的所有存储属性有一个值,这些存储属性内存初始化完成
      • 指定初始化器切换到父类的初始化器为它的存储属性执行同样操作
      • 上一个步骤会顺着继承链向上传递直到基类
      • 一旦到达基类且所有存储属性都有值,实例的内存才被认为完整初始化,阶段一完成

- 阶段二
    + 从基类向下每个指定初始化器完成自己实例的自定义操作;初始化器此时可以访问 `self` 、修改自己的属性、调用实例方法等等等
    + 最后,在继承链中任一便捷初始化器有自定义实例和使用 `self` 选项

  • 初始化器的继承和重写
    • Swift 默认不继承父类初始化器,在某些特定的情况下(安全和合适)才会被继承
    • 如果子类想自定义一个或多个父类的初始化器,可以提供自定义的实现
    • 如果在重写父类的指定初始化器,要加修饰符 override
    • 根据上面的两个阶段规则,子类在重写父类的便捷初始化器不需要 override 修饰,因为不会直接调用父类的便捷初始化器
    class Vehicle {
        var numberOfWheels = 0
        var description: String {
            return "\(numberOfWheels) wheel(s)"
        }
    }
    // Vehicle 没有提供自定义的初始化器,会有一个默认的初始化器且是指定初始化器
    let vehicle = Vehicle()
    print("Vehicle: \(vehicle.description)")
    // Vehicle: 0 wheel(s)
    class Bicycle: Vehicle {
        override init() {
            super.init()
            numberOfWheels = 2
        }
    }
    let bicycle = Bicycle()
    print("Bicycle: \(bicycle.description)")
    // Bicycle: 2 wheel(s)
    • 如果子类初始化器在第二阶段没有执行自定义操作且父类有一个无参的指定初始化器,则子类可以在为所有存储属性赋值后省略 super.init() 调用
    class Hoverboard: Vehicle {
        var color: String
        init(color: String) {
            self.color = color
            // super.init() implicitly called here
        }
        override var description: String {
            return "\(super.description) in a beautiful \(color)"
        }
    }
    let hoverboard = Hoverboard(color: "silver")
    print("Hoverboard: \(hoverboard.description)")
    // Hoverboard: 0 wheel(s) in a beautiful silver
  • 自动初始化器继承
    • 假定子类为每一个属性设置一个默认值,那只需遵循以下两条规则:
    • 规则一:如果子类没有定义任何指定初始化器,将会自动继承父类的所有指定初始化器
    • 规则二:如果子类提供父类的所有指定初始化器的实现(要么按照规则一继承,要么作为定义的一部分提供自定义实现)它将继承父类所有便捷初始化器
    • 即使子类添加了更便捷的初始化器,以上规则仍然适用
    • 根据上面规则二可推导出:子类可以实现父类的指定初始化器作为自己的便捷初始化器
  • 指定和便捷初始化器的应用

    class Food {
        var name: String
        init(name: String) {
            self.name = name
        }
        convenience init() {
            self.init(name: "[Unnamed]")
        }
    }
    let namedMeat = Food(name: "Bacon")
    // namedMeat's name is "Bacon"
    let mysteryMeat = Food()
    // mysteryMeat's name is "[Unnamed]"
    // 根据上面规则 RecipeIngredient 实现了父类的所有初始化器,因此要继承父类所有便捷初始化器
    class RecipeIngredient: Food {
        var quantity: Int
        init(name: String, quantity: Int) {
            self.quantity = quantity
            super.init(name: name)
        }
        override convenience init(name: String) {
            self.init(name: name, quantity: 1)
        }
    }
    let oneMysteryItem = RecipeIngredient()
    let oneBacon = RecipeIngredient(name: "Bacon")
    let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
    // ShoppingListItem没有定义初始化器,因此自动继承父类的所有指定和便捷初始化器
    class ShoppingListItem: RecipeIngredient {
        var purchased = false
        var description: String {
            var output = "\(quantity) x \(name)"
            output += purchased ? " ✔" : " ✘"
            return output
        }
    }
    var breakfastList = [
        ShoppingListItem(),
        ShoppingListItem(name: "Bacon"),
        ShoppingListItem(name: "Eggs", quantity: 6),
    ]
    breakfastList[0].name = "Orange juice"
    breakfastList[0].purchased = true
    for item in breakfastList {
        print(item.description)
    }
    // 1 x Orange juice ✔
    // 1 x Bacon ✘
    // 6 x Eggs ✘
    // 以上继承关系图如下

  • 可失败的初始化器
    • 可失败初始化器是在定义方式为: init?()

      struct Animal {
          let species: String
          init?(species: String) {
              if species.isEmpty { return nil }
              self.species = species
          }
      }
      let someCreature = Animal(species: "Giraffe")
      // someCreature is of type Animal?, not Animal
      //
      if let giraffe = someCreature {
          print("An animal was initialized with a species of \(giraffe.species)")
      }
      // Prints "An animal was initialized with a species of Giraffe"
      let anonymousCreature = Animal(species: "")
      // anonymousCreature is of type Animal?, not Animal
      // 
      if anonymousCreature == nil {
          print("The anonymous creature could not be initialized")
      }
      // Prints "The anonymous creature could not be initialized"
    • 枚举可失败初始化器的定义

      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
              }
          }
      }
      let fahrenheitUnit = TemperatureUnit(symbol: "F")
      if fahrenheitUnit != nil {
          print("This is a defined temperature unit, so initialization succeeded.")
      }
      // Prints "This is a defined temperature unit, so initialization succeeded."
      // 
      let unknownUnit = TemperatureUnit(symbol: "X")
      if unknownUnit == nil {
          print("This is not a defined temperature unit, so initialization failed.")
      }
      // Prints "This is not a defined temperature unit, so initialization failed."
    • 带有原始值枚举的可失败初始化器

      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.")
      }
      // Prints "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.")
      }
      // Prints "This is not a defined temperature unit, so initialization failed."
    • 初始化失败传递

      class Product {
          let name: String
          init?(name: String) {
              if name.isEmpty { return nil }
              self.name = name
          }
      }
      // 
      class CartItem: Product {
          let quantity: Int
          init?(name: String, quantity: Int) {
              if quantity < 1 { return nil }
              self.quantity = quantity
              super.init(name: name)
          }
      }
      if let twoSocks = CartItem(name: "sock", quantity: 2) {
          print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
      }
      // Prints "Item: sock, quantity: 2"
      if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
          print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
      } else {
          print("Unable to initialize zero shirts")
      }
      // Prints "Unable to initialize zero shirts"
      if let oneUnnamed = CartItem(name: "", quantity: 1) {
          print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
      } else {
          print("Unable to initialize one unnamed product")
      }
      // Prints "Unable to initialize one unnamed product"
    • 可失败初始化器重写
      • 像其它初始化器一样,可以重写父类保失败初始化器且可以重写为不可失败的初始化器,此时要强制解包可失败初始化器的结果
      • 不能将不可失败初始化器重写为可失败初始化器
      class Document {
          var name: String?
          // this initializer creates a document with a nil name value
          init() {}
          // this initializer creates a document with a nonempty name value
          init?(name: String) {
              if name.isEmpty { return nil }
              self.name = name
          }
      }
      class AutomaticallyNamedDocument: Document {
          override init() {
              super.init()
              self.name = "[Untitled]"
          }
          override init(name: String) {
              super.init()
              if name.isEmpty {
                  self.name = "[Untitled]"
              } else {
                  self.name = name
              }
          }
      }
      class UntitledDocument: Document {
          override init() {
              super.init(name: "[Untitled]")! // 此处强制解包
          }
      }
    • 可失败初始化隐式解包 (init!)
      • 定义一个隐式解包可失败初始化器,用 init! ,可以用 init! 重写 init? 反之亦可,可以委托 init?init!, 反之亦可;当从 init 委托给 init!,如果初始化失败会触断言
  • 必须的初始化器
    • required 修饰符的初始化器,子类必须要实现,且重写时不需要 override 修饰

      class SomeClass {
          required init() {
              // initializer implementation goes here
          }
      }
      class SomeSubclass: SomeClass {
          required init() {
              // subclass implementation of the required initializer goes here
          }
      }
  • 用闭包或函数为属性设置默认值
    • 用闭包设置默认值,闭包执行在实例的属性初始化之前,所以闭包内不能显示调用 self、属性(包括有默认值)、实例方法,模板大概如下

      class SomeClass {
          let someProperty: SomeType = {
              // create a default value for someProperty inside this closure
              // someValue must be of the same type as SomeType
              return someValue
          }()
      }
      // 闭包简单应用实例:
      struct Chessboard {
          let boardColors: [Bool] = {
              var temporaryBoard = [Bool]()
              var isBlack = false
              for i in 1...8 {
                  for j in 1...8 {
                      temporaryBoard.append(isBlack)
                      isBlack = !isBlack
                  }
                  isBlack = !isBlack
              }
              return temporaryBoard
          }()
          func squareIsBlackAt(row: Int, column: Int) -> Bool {
              return boardColors[(row * 8) + column]
          }
      }
      let board = Chessboard()
      print(board.squareIsBlackAt(row: 0, column: 1))
      // Prints "true"
      print(board.squareIsBlackAt(row: 7, column: 7))
      // Prints "false"

    [Swift从入门到精通]


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Swift从入门到精通第十篇 - 继承 初识发布时间:2022-07-14
下一篇:
Swift从入门到精通第十四篇 - 错误处理 初识发布时间:2022-07-14
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap