Swift学习( 十一:构造过程(1))

构造过程是使用类、结构体或枚举类型的实例之前的准备过程。通过定义构造器来实现构造过程。swift的构造器不需要返回值。

1.存储属性的初始赋值

类和结构体在创建实例时,必须为所有存储属性设置合适的初始值。

  • 构造器
    init(){
    //.....
    }
2.自定义构造过程
  • 构造参数

      init(frome form:Double){
              form = 23.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 mage = Color(red:1.0,green:0.7,blue:1.0)
    let hegray = Color(white:0.5)

注意:如果不通过外部参数名传旨,是没办法调用构造器的。

    let verygrrem = Color(0.0,0.5,1.0)
    //编译报错,需要外部名称
  • 不带外部名称的构造器参数
    如果不希望为构造器提供外部参数,可以使用_来描述外部名

      struct Celsius{
              var temp:Double
              init( _ clel:Double)
      }
    
      let bodytemp = Celsius(37.0)
    
3. 默认构造器

如果结构体或类的所有属性都有默认值,同时没有自定义构造器,系统则会提供一个默认构造器。默认构造器会简单的创建一个所有属性值都设置为默认值的实例

    class shop {
            var anme:String?
            var quatiy = 1
            var purh = false
    }
    var item = shop()

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

    struct Size {
            var width = 0.0, height = 0.0
    }
    let twobytwo = Size(width:2.0,height:3.0)

类里面的所有属性(包括所有继承自父类的属性)都必须在构造过程中设置初始值。

4. 指定构造器和便利构造器

指定构造器初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。每一个来都必须拥有至少一个指定构造器。
便利构造器可以调用同一个类中的指定构造器,并为其参数提供默认值。

    指定构造器
    init(){

    }
    
    便利构造器
    convenience init(){

    }

指定构造器必须调用其直接父类的指定构造器。
便利构造器必须顶用同一类中定义的其他构造器
便利构造器必须最终导致一个指定构造器被调用。

便利构造器和指定构造器的关系
更复杂一点的关系
5.两段式构造过程

分两个阶段:第一个阶段,每个存储型属性被引入它们的类指定一个初始值。当每个存储型属性的初始值被确定后,第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步定制它们的存储型属性。
两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问,也可以防止属性被另外一个构造器意外地赋予不同的值。

    Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能不出错地完成:
    1.指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。

    如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器必须保证它所在类引入的属性在它往上代理之前先完成初始化。

    2.指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。

    3.便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。

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

    类实例在第一阶段结束以前并不是完全有效的。只有第一阶段完成后,该实例才会成为有效实例,才能访问属性和调用方法。

以下是两段式构造过程中基于上述安全检查的构造流程展示:

阶段 1
  • 某个指定构造器或便利构造器被调用。
  • 完成新实例内存的分配,但此时内存还没有被初始化。
  • 指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化。
  • 指定构造器将调用父类的构造器,完成父类属性的初始化。
  • 这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部。
  • 当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段 1 完成。
阶段 2
  • 从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。
  • 最终,任意构造器链中的便利构造器可以有机会定制实例和使用self。
6.构造器的继承和重写
  • swift默认情况下不会继承父类的构造器。
    重写父类构造器的指定构造器时,必须在定义子类构造器时带上override修饰符。* override修饰符会让编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否正确。
  • 如果编写了一个和父类便利构造器相匹配的子类构造器,而子类构造器不能直接调用父类的便利构造器,所以,在重写父类便利构造器时,不用加override
7.构造器的自动继承
  • 如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
  • 如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。

注意:子类可以将父类的指定构造器实现为便利构造器。

推荐阅读更多精彩内容