Swift学习:构造器(中)

本篇继续对Swift中的构造器进行介绍,这里主要说到类的继承和构造。 作为引用类型的类具有的继承特性,这使得类的构造过程较为复杂一些,因为涉及到很多细节性的问题。在使用之前,我们需要了解一些基本的问题:

  • 类中所有存储属性,包括继承父类的属性,都要在构造过程中设置初值
  • Swift类的构造器分为指定构造器和便利构造器,确保完成构造过程

一、指定构造器和便利构造器

指定构造器:
  • 类的主要构造器,负责初始化类中所有属性,在继承关系中可调用父类链中的父类构造器
  • 每个类至少一个指定构造器。但是某些情况下,许多类是通过继承父类的指定构造器来满足这个条件
便利构造器:
  • 辅助类型的构造器,调用同一个类中的指定构造器完成类的初始化操作
  • 便利构造器需要在init关键字之前添加convenience关键字,使用空格分开
class FatherClass {
    var valueOne: Int
    //指定构造器
    init(valueOne: Int){
        self.valueOne = valueOne
    }
}

class ChildClass: FatherClass {
    var valueTwo:Int //子类的新引入属性
    //指定构造器
    init(valueOne: Int , valueTwo:Int){
        self.valueTwo = valueTwo
        //先初始化新引入属性,再初始化超类
        super.init(valueOne: valueOne)
    }
    //便利构造器
    override convenience init(valueOne: Int){
        self.init(valueOne: valueOne);
    }
    
}

二、类的构造器的使用

关于类的构造器的使用在使用的时候,细节方面需要要注意很多,通过查找一些资料,现总结如下:

1. 指定构造器和便利构造器的基本使用原则:

规则1:当前类存在父类时,指定构造器器必须调用其直接父类的指定构造器,为保证继承的属性得以初始化。 规则2:便利构造器必须调用同一类中定义的其他构造器。而且最终会导致一个指定构造器被调用。

2. Swift两段式构造过程

Swift 中类的构造过程包含两个阶段,被称为是二段式构造 第一阶段:每个存储型的值指定一个初值。 第二阶段:给当前类一个机会,在新实例准备使用之前进一步修改定制存储型的属性。 与OC的区别:Swift与OC的构造过程相似,区别在于阶段一,OC给每个属性赋值为0或者空值,但是Swift更为灵活,允许开发者指定自己所需的初始值(默认值)。

3. 两段式构造需要注意的事项
  • 指定构造器必须保证当前类新引入所有属性初始化完成之后,才能将其构造器任务继续向上代理给父类的构造器。
  • 指定构造器必须先向上代理调用父类构造器,然后再为其继承的属性设置新值,否则,指定构造器赋予的新值将被父类中的构造器所覆盖。
  • 便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。否则,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。
  • 构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用self 作为一个值,因为此时还没构造有效实例。
4. 总结两段式构造的详细流程

第一阶段:

  • 某个指定构造器或便利构造器被调用。
  • 完成新实例内存的分配,但此时内存还没有被初始化。
  • 指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化。
  • 指定构造器将调用父类的构造器,完成父类属性的初始化。这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部。
  • 当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段 1 完成。

第二阶段: 从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。 最终,任意构造器链中的便利构造器可以有机会定制实例和使用self 。

三、构造器的继承与重写

  1. 与OC不同,Swift中的子类默认情况不会自动继承父类的构造器,这是因为子类可能有更多新增属性,直接调用父类的构造器,可能会有一些属性无法初始化为出现错误。
  2. 如果我们希望子类拥有一个或多个与父类相同的构造器,那么这相当于子类重写了父类的构造器,需要在重写方法前添加override修饰符。(override的作用是提示编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否正确)

注意1: 重写系统自带的默认构造器(虽然不是显式的),也要带上override修饰符 注意2: 重写父类指定构造器必须带上override,即使你的子类将父类的指定构造器重写成了便利构造器 注意3: 子类编写和父类便利构造器相匹配的子类构造器时,由于子类不能直接调用父类的便利构造器,并不能看做是对父类构造器的重写。所以子类中“重写”父类便利构造器时,不需要加override修饰符。

四、构造器的自动继承

子类在默认情况下不会继承父类的构造器。但是如果满足特定条件,父类构造器还是是可以被自动继承的。 构造器的自动继承需要满足前提条件是:子类中引入的所有新属性都提供了默认值。 然后可分为两种情况:

情况1:子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。

class SuperClass {
    var valueOne: Int
    //指定构造器
    init(valueOne: Int){
        self.valueOne = valueOne
    }
}

class SubClass: SuperClass {
    var valueTwo:Int  = 100 //子类的新引入属性
}

//自动继承了父类的构造器,这里被使用创建子类,子类新属性使用默认值
let subClass = SubClass(valueOne: 10)
print("subClass:(\(subClass.valueOne)) valueTwo(\(subClass.valueTwo))")

情况2:子类提供了所有父类指定构造器的实现(通过情况1继承过来的或者子类自定义实现),此时子类将自动继承所有父类的便利构造器。

class Animal{
    var name:String
    var age: Int
    init(name:String, age:Int){
        self.name = name
        self.age = age
    }
    //父类的便利构造器
    convenience init(name:String){
        self.init(name:name, age:10)
    }
}

class Person: Animal{
    var nickName:String!
    //实现了父类中的指定构造器
    override init(name:String, age:Int){
        super.init(name:name, age:age)
        self.name = name
        self.age = age + 2
    }
}

let ps = Person(name: "zs", age: 18)

自动继承注意: 1.即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。 2.对于情况 2,子类可以将父类的指定构造器实现为便利构造器。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏WindCoder

在数组中查找次大值,并与最后一个元素交换—C语言

1741
来自专栏ShaoYL

结构体

40213
来自专栏机器学习算法与Python学习

python: sort, sorted, reverse

python语言中的列表排序方法有三个:reverse反转/倒序排序、sort正序排序、sorted可以获取排序后的列表。在更高级列表排序中,后两中方法还可以加...

3678
来自专栏微信公众号:Java团长

浅谈Java中的equals和==

  为什么第4行和第5行的输出结果不一样?==和equals方法之间的区别是什么?如果在初学Java的时候这个问题不弄清楚,就会导致自己在以后编写代码时出现一些...

891
来自专栏后端技术探索

当一只程序员遇到了一道无聊的智力填数题!

本猿在朋友圈和群里多次看到这样一道智力题(见下图),一看就是一道需要乱扯的无聊的题目。好吧,试试就试试。

751
来自专栏编程

Python基础知识2:字典

字典一种key - value 的数据类型,就像上学用的字典通过拼音查找汉字一样;字典是Python语言中唯一的映射类型。字典对象是可变的,它是一个容器类型,能...

23210
来自专栏Java 源码分析

静态内部类

定义:静态内部类,定义在类中,任何方法外,用static定义;静态内部类只能访问外部类的静态成员。 注意点: 一般情况下,如果一个内部类不是被定义成静态内部类,...

3168
来自专栏猿人谷

const用法小结

常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。因此,定义或说明常类型时必须进行初始化。 概述 1. const有什么...

1857
来自专栏程序手艺人

C++之初始化列表

3066
来自专栏吴伟祥

Java类初始化顺序 转

关于构造函数,以下几点要注意: 1.对象一建立,就会调用与之相应的构造函数,也就是说,不建立对象,构造函数时不会运行的。 2.构造函数的作用是用于给对象进行...

993

扫码关注云+社区

领取腾讯云代金券