Swift基础篇- 类的初始化和反初始化

1,052 阅读9分钟

1.初始化器

1.1初始化器在创建特定类型时被调用

struct Fathrenheit {

var temperture : Double

init() {
   temperture = 32
}
}

var f = Fathrenheit()

print("The default temperature is \(f.temperture) fathreheit")

1.2.默认的属性值

如上所述,可以在初始化器里为存储属性设置初始值。另外,指定一个默认属性值作为属性声明的一部分。当属性被定义的时候可以通过为这个属性分配一个初始值来指定默认的属性值

struct Fathrenheit {
   var temperture = 32.0
}
var f = Fathrenheit()
print("The default temperature is \(f.temperture) fathreheit")

1.3.默认的初始化器

Swift为所有没有提供初始化器的结构体或类提供一个默认的初始化器来给所有的属性提供了默认值。这个默认的初始化器只是简单的创建了一个所有属性都有默认值的新实例

class ShoppingListItem {
  var name : String?
  var quantity = 1
  var purchased = **false**
}

**var** item = ShoppingListItem()

1.4自定义初始化器

可以提供初始化形式参数作为初始化器的一部分,来定义初始化过程中的类型和值得名称。初始化形式参数与函数和方法的形式参数具有相同的功能和语法

struct Celsius {

var temperatureInCelsius: Double

init(fromFathrenheit fahtenheit: Double){

temperatureInCelsius = (fahtenheit - 32.0) / 1.8

}

init(formKelvin kelvin:Double) {

temperatureInCelsius = kelvin - 237.15

}

}

let boilingPointOfWater = Celsius(formKelvin: 212.0)

let freezingPointOfWater = Celsius(formKelvin: 273.15)

1.5.在初始化中分配常量属性

在初始化的任意时刻,都可以给常量属性赋值,只要它在初始化结束时设置了确定的值即可。一旦为常量属性被赋值,他就不能在被修改了

class SurveQuestion {
   let text: String
   var response : String?
   init(text : String) {
     self.text = text
   }

  func ask() {
    print(text)
  }
}
let beetQuestion = SurveQuestion(text: "How about beets?")

beetQuestion.ask()

beetQuestion.response = "I also like beets.(But nor with cheese)"

2.结构体的成员初始化器

如果结构体类型中没有定义任何自定义初始化器,他会自动获得一个成员初始化器。不同于默认初始化器,结构体会接受成员初始化器即使它的存储属性没有默认值

struct size {
  var width = 0.0 ,heighr = 0.0
}

let twoByTwo = size(width: 2.0, heighr: 2.0)

3.值类型的初始化器委托

初始化器可以调用其他初始化器来执行部分实例的初始化。这个过程,就是所谓的初始化器委托,避免了多个初始化器里冗余代码

struct Size {
  var width = 0.0 ,heighr = 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.heighr / 2)
     self.init(origin: Point(x:originX,y:originY), size: size)
  }
}

4.类的继承和初始化

4.1相关描述

  1. 所有类的存储属性(包括 他的父类继承的所有属性)。都必须在初始化期间分配初始值
  2. Swift为类型定义了两种初始化器以确保所有的存储属性接受一个初始值。这些就是所谓的指定初始化器和便捷初始化器
  3. 指定初始化器是类的主要初始化器。指定的初始化器可以初始化所有那个类引用的属性并且调用合适的父类初始化器继续初始化过程给父类链
  4. 类偏向于少量指定初始化器,并且一个类通常只有一个指定初始化器。指定初始化器是初始化开始持续初始化过程到父类链的“传送“点
  5. 每个类至少得有一个指定初始化器,如同在初始化器的自动继承里面描述的那样,在某些情况下,这些需求通过父类继承一个或多个指定初始化器来满足
  6. 便捷初始化器是次要的。可以在相同的类里定义一个便捷初始化器来调用一个指定的初始化器作为便捷初始化器来给指定初始化器设置默认的形式参数。也可以为具体的使用情况或输入的值类型定义一个便捷初始化器,从而创建这个类的实例
  7. 如果你的类不需要便捷初始化器可以不提供它。在为通用的初始化模式创建快捷方式,以节省或者类的初始化更加清晰明了的时候使用便捷初始化器

4.2.指定初始化器和便捷初始化器

init(parameters) {
    statements
}

convenience** init(parameters){
    statements
}

4.3.类的初始化委托

指定的初始化器必须从他的直系父类调用指定初始化器

便捷初始化器必须从相同的类里调用另一份初始化器

便捷初始化器最终必须调用一个初始化器

image.png

4.4两段式初始化

Swift 的类初始化是一个两段式过程。在第一阶段,每个存储属性被引入类分配了一个初始值。一旦每个存储属性的初始状态被确定,第二个就开始了,每个类都有机会在新的实例准备之前来定制他的存储属性。

两段式初始化过程的使用让初始化更安全,同时每个类的层级结构给与了完备的灵活性。两段式初始化过程可以防止属性在初始化话之前被访问,还可以防止属性被另一个初始化器意外地赋予不同值

安全检查

1.指定的初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性都要初始化完成

2.指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值。如果不这样做,指定初始化器赋予的新值将被父类中的初始化器所覆盖

3.便捷初始化器必须先委托同类中的其他初始化器,然后再为任意属性赋值(包括同类里定义的属性)。如果没有这么做,便捷构造初始化器赋予新值将被自己类中其他指定初始化器所覆盖

4.初始化器在第一阶段初始化完成之前,不能调用任何实例方法,不能读取任何实例属性的值,也不能引用self作为值

class Peson {
   var name : String
   var age : Int
   init(name:String,age:Int) {
      self.name = name
      self.age = age
   }
   convenience init() {
      self.init(name: "[Unnamed]", age: 0)
   }
}

class Teacher : Peson {
    var salary : Int
    init(name:String,age:Int,salary:Int){

/* self.salary = salary :指定的初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性都要初始化完成 */

    self.salary = salary

    super.init(name: name, age: age)

/* self.name = name + "老师" :指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值*/

    self.name = name + "老师"

    self.test()

}

convenience override init(name: String, age: Int) {

    self.init(name: name, age: 30, salary: 5000)

/* self.name = name + "老师":
   便捷初始化器必须先委托同类中的其他初始化器,然后再为任意属性赋值(包括同类里定义的属性
*/

    self.name = name + "老师"

}

    func test() {
       print("i am a teacher")
    }

    func showInfo() {
       print("teacher name \(name),age \(age), salary \(salary)")
    }

}
6.4.4.1.阶段一

1、指定或便捷初始化器在类中被调用

2、为了这个类的新实例内存分配。内存还没被初始化

3.这个类的指定初始化器确保所有由此类引入的存储属性都有一个值。现在这些存储属性的内存被初始化

4.这个调佣父类初始化器的过程沿着初始化器链一直向上进行,直到到达初始化器的最顶部

5.一旦到了初始化器链的最顶部,在链顶部的类确保所有额存储属性都有一个值,此实例的内存被认为完全初始化,此时第一阶段完成

6.4.4.2.阶段二

1.从顶部初始化器往下,链中的每一个指定初始化器都有机会进一步定制实例。初始化器现在能访问self并且可以修改他的属性,调用它的实例方法等等

2.最终,链中任何便捷初始化器都有机会定制实例以及使用self

6.5.初始化器的继承和重写

  • 不像在OC中的子类,Swift的子类不会默认继承父类的初始化器。Swift的这种机制防止父类的简单初始器被一个专用的子类继承并被用来创建一个完全错位初始化的新实例的情况发生。只有在特定的情况下才会继承父类的初始化器。
  • 如果想自定义子类来实现一个或多个父类相同的初始化,可以在子类中为那些初始化器提供定制实现
  • 当你写的子类初始化器匹配父类指定初始化器的时候,实际上可以重写那个初始化器。因此,在子类的初始化器定义之前必须写override修饰符。如同默认初始化器所描述的那样,即使是自动提供默认初始化器也可以重写

6.5.1.初始化器的自动继承

规则:

  • 如果子类没有定义任何的初始化器,他会自动继承父类所有的的指定初始化器
  • 如果子类提供了所有父类指定初始化器的实现--要么通过规则1来继承,要么通过在定义中提供自定义实现的--那么他自动继承所有的父类便捷初始化器

6.5.2.可失败初始化器

  • 定义类、结构体或枚举初始化时可以失败在某些情况下会管大用。这种失败可能有几种方式出发,包括初始化传入无效参数,或缺少某种外部所需的资源,又或者其他阻止初始化的情况
  • 为了妥善处理这种可能失败的情况,在类、结构体中定义一个或多个可失败的初始话器。通过init关键字后面的添加问号(init?)来写
6.5.3.必须初始化器

在类的初始化器添加required 修饰符来表明所有该类的子类都必须实现该初始化器

6.6.反初始化

  • 在类的实例被释放的时候,返初始化器就会被立即调用。你可以使用deninit关键字来写反初始化器,就如同写初始化器要用init关键字一样。反初始化器只有在类类型中有效
  • 返初始化器会在实例被释放之前自动被调用。不能自行调用返初始化器。父类的反初始化器可以被子类继承,并且子类的反初始化器总会被调用,就算子类没有反初始化器
  • 每个类中只能有一个反初始化器。返初始化器不接受任何形式的参数,并且不需要写圆括号