Swift5.x入门13--继承,final,初始化器,可选链

  • 值类型(枚举,结构体)不支持继承,只有类支持继承;
  • 没有父类的类,称之为基类
  • 子类可以重写父类的下标方法属性,重写时必须加上override关键字;

内存结构

import Foundation

class Animal {
    var age: Int = 0
}

class Dog: Animal {
    var weight = 0
}

class ErHa: Dog {
    var iq = 0
}

let a = Animal()
a.age = 10

let d = Dog()
d.age = 11
d.weight = 20

let eh = ErHa()
eh.age = 15
eh.weight = 30
eh.iq = 2
  • 三种实例对象的内存结构如下:
Snip20210802_103.png

重写实例方法,下标

import Foundation

class Animal {
    var age: Int = 0
    func speak() -> Void {
        print("Animal speak")
    }
    
    subscript(index: Int) -> Int{
        return index
    }
}

class Dog: Animal {
    var weight = 0
    //重写实例方法
    override func speak() {
        super.speak()
        print("Dog speak")
    }
    //重写下标
    override subscript(index: Int) -> Int{
        return super[index]+1
    }
}

let a = Animal()
a.age = 10
a.speak() //Animal speak
print(a[6]) //6

let d = Dog()
d.age = 11
d.weight = 20
d.speak() //Animal speak Dog speak
print(d[6]) //7

重写类型方法,类型下标

  • class修饰的类型方法,下标,允许被子类重写;
  • static修饰的类型方法,下标,不允许被子类重写;
class Animal {
    var age: Int = 0
    class func speak() -> Void {
        print("Animal speak")
    }
   
    class subscript(index: Int) -> Int{
        return index
    }
}

class Dog: Animal {
    var weight = 0
    //重写类型方法
    override class func speak() {
        super.speak()
        print("Dog speak")
    }
    //重写类型下标
    override class subscript(index: Int) -> Int{
        return super[index]+1
    }
}

Animal.speak()
Animal[6]

Dog.speak()
Dog[6]
  • 编译通过;
class Animal {
    var age: Int = 0
    static func speak() -> Void {
        print("Animal speak")
    }
   
    static subscript(index: Int) -> Int{
        return index
    }
}

class Dog: Animal {
    var weight = 0
    override class func speak() {
        super.speak()
        print("Dog speak")
    }
    
    override class subscript(index: Int) -> Int{
        return super[index]+1
    }
}

Animal.speak()
Animal[6]

Dog.speak()
Dog[6]
  • 出现报错,子类Dog不能重写父类Animal的类型方法,下标;
class Animal {
    var age: Int = 0
    class func speak() -> Void {
        print("Animal speak")
    }
   
    class subscript(index: Int) -> Int{
        return index
    }
}

class Dog: Animal {
    var weight = 0
    override static func speak() {
        super.speak()
        print("Dog speak")
    }
    
    override static subscript(index: Int) -> Int{
        return super[index]+1
    }
}

Animal.speak()
Animal[6]

Dog.speak()
Dog[6]
  • 编译通过;

重写属性

  • 子类可以将父类的属性(存储,计算属性),重写为计算属性
  • 子类不可以将父类属性重写为存储属性;
  • 只能重写var属性,不能重写let属性
  • 重写时,属性名,类型要保持一致;
  • 子类重写后属性权限,不能小于父类的属性权限;
    • 如果父类的属性是只读的,那么子类重写后的属性可以是只读的,也可以是可读可写的;
    • 如果父类的属性是可读可写的,那么子类重写后的属性必须是可读可写的;
重写实例属性
class Cirle {
    var radius: Int = 0
    var dimameter: Int {
        set {
            print("Cirle setDimameter")
            radius = newValue / 2
        }
        get {
            print("Cirle getDimameter")
            return radius * 2
        }
    }
}

class SubCirle: Cirle{
    //将存储属性 重写为 计算属性
    override var radius: Int {
        set {
            print("SubCirle setDimameter")
            super.radius = newValue > 0 ? newValue : 0
        }
        get {
            print("subCirle getDimameter")
            return super.radius
        }
    }
    //重写计算属性
    override var dimameter: Int {
        set {
            print("SubCirle setDimameter")
            super.dimameter = newValue > 0 ? newValue : 0
        }
        
        get {
            print("SubCirle getDimameter")
            return super.dimameter
        }
    }
}
重写类型属性
  • class修饰的类型属性,可以被子类重写,且可以重写为static关键字修饰,保证不被下一级子类再重写;
  • static修饰的类型属性(存储,计算),不可以被子类重写
class Cirle {
    static var radius: Int = 0
    class var dimameter: Int {
        set {
            print("Cirle setDimameter")
            radius = newValue / 2
        }
        get {
            print("Cirle getDimameter")
            return radius * 2
        }
    }
}

class SubCirle: Cirle{
    override static var dimameter: Int {
        set {
            print("SubCirle setDimameter")
            super.dimameter = newValue > 0 ? newValue : 0
        }
        
        get {
            print("SubCirle getDimameter")
            return super.dimameter
        }
    }
}

重写属性观察器

  • 可以在子类中为父类的属性(除了只读计算属性,let属性),添加属性观察器;

class Cirle {
    var radius: Int = 0
}

class SubCirle: Cirle{
    override var radius: Int {
        willSet {
            print("SubCirle willSetRadius",newValue)
        }
        
        didSet {
            print("SubCirle didSetRadius",oldValue,radius)
        }
    }
}
  • 在SubCirle类中,给父类Cirle的radius属性,添加属性观察器;
class Cirle {
    var radius: Int = 0 {
        willSet {
            print("Cirle willSetRadius",newValue)
        }
        
        didSet {
            print("Cirle didSetRadius",oldValue,radius)
        }
    }
}

class SubCirle: Cirle{
    override var radius: Int {
        willSet {
            print("SubCirle willSetRadius",newValue)
        }
        
        didSet {
            print("SubCirle didSetRadius",oldValue,radius)
        }
    }
}
  • 子类可以重写父类属性的属性观察器

final

  • final修饰的方法,下标,属性,禁止被子类重写;
  • final修饰的类,禁止被继承;

多态的实现原理

  • 针对结构体而言,编译完成,结构体实例调用的方法是确定的,因为它不存在继承,不存在重写;
struct Animal {
    func speak() -> Void {
        print("Animal speak")
    }
    func eat() -> Void {
        print("Animal eat")
    }
    func sleep() -> Void {
        print("Animal sleep")
    }
}

var animal = Animal()
animal.speak()
animal.eat()
animal.sleep()
  • 汇编代码如下:
Snip20210802_104.png
  • 将上面的结构体改成类,代码如下:
class Animal {
    func speak() -> Void {
        print("Animal speak")
    }
    func eat() -> Void {
        print("Animal eat")
    }
    func sleep() -> Void {
        print("Animal sleep")
    }
}

var animal = Animal()
animal.speak()
animal.eat()
animal.sleep()
  • 生成的汇编代码很多,与结构体的完全不同,这是因为类的实例对象调用方法是不确定的,是动态的,因为类存在继承,方法可重写,当前实例可能调用父类的方法,也能调用自己的方法;
Snip20210802_105.png
  • animal.speak(),对应的汇编callq *0x50(%rcx),其函数内存地址是动态的,不是写死的;

  • 将代码改成有继承关系的,如下所示:

class Animal {
    func speak() -> Void {
        print("Animal speak")
    }
    func eat() -> Void {
        print("Animal eat")
    }
    func sleep() -> Void {
        print("Animal sleep")
    }
}

class Dog: Animal {
    override func speak() -> Void {
        print("Dog speak")
    }
    override func eat() -> Void {
        print("Dog eat")
    }
    func run() -> Void {
        print("Dog run")
    }
}

var animal = Animal()
animal.speak()
animal.eat()
animal.sleep()

//父类指针指向子类对象
animal = Dog()
animal.speak()
animal.eat()
animal.sleep()
  • animal.speak()所在行打下断点,汇编代码如下:
    Snip20210802_106.png
  • callq *0x50(%rcx)就是animal.speak()的汇编实现;
  • 首先movq 0x4d5a(%rip), %rax:(rip+ 0x4d5a)=0x1000082D8,就是全局变量animal的内存地址,其内存存储的是dog实例对象的内存地址0x0000000100529c40,然后将dog实例对象的内存地址写入rax寄存器;
  • movq (%rax), %rcx:(%rax)表示取rax中内存地址中的内容,按8个字节去取即dog的类型信息,然后存入rcx寄存器;
  • callq *0x50(%rcx):*(rcx + 0x50)即取出这块的内存地址,也就是speak函数地址,最后call进行调用;
  • 其调用原理如下图所示:
Snip20210802_107.png
  • 可以看出Dog的类型信息是存储在全局区,所有Dog实例对象共享这一份内存;

初始化器

  • 类,结构体,枚举都可以定义初始化器;
  • 类有两种初始化器:指定初始化器便捷初始化器
  • 每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器;
  • 默认初始化器总是类的指定初始化器;
  • 类偏向于少量指定初始化器,一个类通常只有一个指定初始化器;
  • 初始化器相互调用规则:
    • 指定初始化器必须从它的直系父类调用指定初始化器;
    • 便捷初始化器必须从相同的类里调用另一个始化器;
    • 便捷初始化器最终必须调用一个指定初始化器;
class Size {
    var width: Int = 0
    var height: Int = 0
    //指定初始化器,是主要初始化器
    init(width: Int,height: Int) {
        self.width = width
        self.height = height
    }
    
    //三个便捷初始化器
    convenience init(width: Int){
        self.init(width: width,height: 0)
    }
    
    convenience init(height: Int){
        self.init(width: 0,height: height)
    }
    
    convenience init(){
        self.init(width: 0,height: 0)
    }
}

var s1 = Size()
var s2 = Size(width: 10)
var s3 = Size(height: 20)
var s4 = Size(width: 10, height: 20)
  • 定义可一个指定初始化器,三个便捷初始化器,便捷初始化器内部调用指定初始化器,保证实例对象的所有存储属性都有值;
class Person {
    var age: Int
    init(age: Int) {
        self.age = age
    }
    
    convenience init(){
        self.init(age: 0)
    }
}

class Student : Person {
    var score: Int
    //指定指定初始化器
    init(age: Int,score: Int) {
        self.score = score
        //调用父类的指定初始化器
        super.init(age: age)
    }
    
    //便捷初始化器
    convenience init(){
        self.init(score: 0)
    }
    
    //便捷初始化器
    //最终必须调用一个指定初始化器
    convenience init(score: Int){
        self.init(age: 0,score: score)
    }
}
  • Swift在编码安全方面是煞费苦心,为了保证初始化过程的安全,设置了两段式初始化安全检查

两段式初始化

  • 第一个阶段:初始化所有存储属性;
    • 外层调用指定/便捷初始化器;
    • 分配给实例,但未初始化;
    • 指定初始化器必须要确保当前类定义的存储属性都初始化;
    • 指定初始化器调用父类的指定初始化器,不断向上调用,形成初始化器链;
  • 第二个阶段:设置新的存储属性值;
    • 从顶部初始化器往下,链中的每一个指定初始化器都有机会进一步定制实例;
    • 初始化器现在能使用self(访问,修改它的属性,调用它的实例方法等等)
    • 最终,链中任何便捷初始化器都有机会定制实例以及使用self;
class Person {
    var age: Int
    init(age: Int) {
        self.age = age
        
        //进入第二阶段 个性化定制
    }
    
    convenience init(){
        self.init(age: 0)
    }
}

class Student : Person {
    var score: Int
    //指定指定初始化器
    init(age: Int,score: Int) {
        self.score = score
        //调用父类的指定初始化器
        super.init(age: age)
        
        //进入第二阶段 个性化定制
    }
    
    //便捷初始化器
    convenience init(){
        self.init(score: 0)
        
        //进入第二阶段 个性化定制
    }
    
    //便捷初始化器
    //最终必须调用一个指定初始化器
    convenience init(score: Int){
        self.init(age: 0,score: score)
        
        //进入第二阶段 个性化定制
        
    }
}

var stu = Student()

安全检查

  • 指定初始化器必须保证在调用父类指定初始化器之前,其当前类定义的所有存储属性都要初始化完成;
  • 指定初始化器必须先调用父类初始化器,然后才能为继承的属性设置新值;
  • 便捷初始化器必须先调用同类中的其他初始化器,然后再为任意的属性设置新值;
  • 初始化器在第一阶段初始化完成之前,不能调用任何实例方法,不能读取任何实例属性的值,也不能引用self
  • 直到第一阶段结束时,实例才算完全合法;

重写

  • 当重写父类指定的初始化器时,必须加上override关键字(即使子类的实现是便捷初始化器)
class Person {
    var age: Int
    init(age: Int) {
        self.age = age
    }
}

class Student : Person {
    var score: Int
    //指定初始化器
    init(age: Int,score: Int) {
        self.score = score
        //调用父类的指定初始化器
        super.init(age: age)
    }
    
    //重写父类的指定初始化器
//    override init(age: Int) {
//        self.score = 0
//        super.init(age: age)
//    }
    
    //将父类的指定初始化器 重写为便捷初始化器
    override convenience init(age: Int) {
        self.init(age: age,score: 0)
    }
}
  • 子类可以重写父类的指定初始化器需加上override关键字,子类也可以将父类的指定初始化器,重写为便捷初始化器同样要加上override关键字;
  • 子类是不能重写父类的便捷初始化器的,便捷初始化器只能在当前类中横向调用,不能跨越在继承链上调用;
class Person {
    var age: Int
    //指定初始化器
    init(age: Int) {
        self.age = age
    }
    //便捷初始化器
    convenience init(){
        self.init(age: 0)
    }
}

class Student : Person {
    var score: Int
    //指定初始化器
    init(age: Int,score: Int) {
        self.score = score
        //调用父类的指定初始化器
        super.init(age: age)
    }

    //不能加override
    init() {
        self.score = 0
        super.init(age: 0)
    }
}
  • Person的便捷初始化器init()
  • Studentinit(),是不能加override的,因为Student是不能重写Person的便捷初始化器init();

自动继承

  • 第一条:如果子类没有定义任何指定初始化器,它会自动继承父类的所有指定初始化器;
class Person {
    var age: Int
    var name: String
    
    init(age: Int,name: String) {
        self.age = age
        self.name = name
    }
    
    init(age: Int){
        self.age = age
        self.name = ""
    }
}

class Student : Person {
    
    
}

var stu1 = Student(age: 10)
var stu2 = Student(age: 10,name: "li")
  • 第二条:如果子类拥有父类所有指定初始化器的实现(通过继承,重写的方式),那么子类会自动继承父类的所有便捷初始化器;
  • 通过继承的方式就是啥都不干,如下所示:
class Person {
    var age: Int
    var name: String
    init(age: Int,name: String) {
        self.age = age
        self.name = name
    }
    init(){
        self.age = 0
        self.name = ""
    }

    convenience init(age: Int) {
        self.init(age: age,name: "")
    }

    convenience init(name: String) {
        self.init(age: 0,name: name)
    }
}

class Student : Person {
    //自动继承父类所有指定初始化器
    //继承父类所有便捷初始化器

}

var stu1 = Student(age: 10, name: "li")
var stu2 = Student()
var stu3 = Student(age: 10)
var stu4 = Student(name: "li")
  • 通过重写的方式:
class Person {
    var age: Int
    var name: String
    init(age: Int,name: String) {
        self.age = age
        self.name = name
    }
    init(){
        self.age = 0
        self.name = ""
    }

    convenience init(age: Int) {
        self.init(age: age,name: "")
    }

    convenience init(name: String) {
        self.init(age: 0,name: name)
    }
}

class Student : Person {
    //重写父类所有指定初始化器
    override init(age: Int,name: String) {
        super.init(age: age, name: name)
    }
    override init(){
        super.init()
    }

    //继承父类所有便捷初始化器
}

var stu1 = Student(age: 10, name: "li")
var stu2 = Student()
var stu3 = Student(age: 10)
var stu4 = Student(name: "li")
  • 第三条:若子类添加了更多的便捷初始化器,上述两条规则依然适用;
  • 第四条:子类以便捷初始化器的方式也可重写父类的指定初始化器;

required

  • required来修饰指定初始化器,表明其所有的子类必须实现该初始化器,子类可通过继承或者重写的方式来实现;

  • 如果子类重写了required初始化器,也必须加上required,不用加override

  • 通过继承的方式的来实现:啥都不干

class Person {
    required init(){

    }
    init(age: Int) {

    }
}

class Student : Person {
    
}
  • 上述代码不会报错;

  • 通过重写的方式的来实现:

class Person {
    required init(){

    }
    init(age: Int) {

    }
}

class Student : Person {
    //自定义指定初始化器
     init(no: Int) {
        super.init(age: 0)
    }

    required init() {
        super.init()
    }
}
  • 由于Student自定义了指定初始化器,那么父类所有指定初始化器就不能自动继承了,所以Student需要手动去实现required init() ;

属性观察器

  • 父类的属性在它自己的初始化器中赋值时,不会触发属性观察器,但在子类的初始化器中赋值会触发属性观察器;
class Person {
    var age: Int {
        willSet {
            print("willSet",newValue)
        }
        didSet {
            print("didSet",oldValue,age)
        }
    }
    init() {
        //不会触发属性观察器
        self.age = 0
    }
}

class Student : Person {
    override init() {
        super.init()
        //触发属性观察器
        self.age = 1
    }
}

var stu = Student()

可失败初始化器

  • 类,结构体,枚举都可以使用init?定义可失败初始化器;
class Person {
    var name: String
    //可失败初始化器
    init?(name: String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
}
var person = Person(name: "") //person是可选项类型
  • 初始化器初始化实例可能出现失败,返回nil;
  • person实例时可选项类型,即Person?
  • 不允许同时定义参数标签,参数个数,参数类型相同的可失败初始化器与非可失败初始化器;
class Person {
    var name: String
    init?(name: String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }

    //报错 init?(name: String)与init(name: String) 不能同时存在
    init(name: String) {
        self.name = name
    }
}
  • 可以用init!定义隐式解包的可失败初始化器;
  • 可失败初始化器可调用非可失败初始化器;
class Person {
    var name: String
    //可失败初始化器
    convenience init?(name: String) {
        //可失败初始化器调用非可失败初始化器
        self.init()
        if name.isEmpty {
            return nil
        }
    }
    //非可失败初始化器
    init() {
        self.name = ""
    }
}
  • 非可失败初始化器调用可失败初始化器,需要进行解包;
class Person {
    var name: String
    //可失败初始化器
    init?(name: String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }

    //非可失败初始化器
    convenience init() {
        //非可失败初始化器调用可失败初始化器,需要进行解包
        self.init(name: "")!
    }
}
  • 如果初始化器调用一个可失败初始化器导致初始化失败,那么整个初始化过程都失败,并且之后的代码中止执行;
class Person {
    var name: String
    //可失败初始化器
    init?(name: String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
    //可失败初始化器
    convenience init?() {
        //调用可失败初始化器
        self.init(name: "") //若失败 中止执行
        self.name = "li"
    }
}
  • 可以用一个非可失败初始化器重写一个可失败初始化器,反之不行;
class Person {
    var name: String
    //可失败初始化器
    init?(name: String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
}

class Student : Person {
    //重写成非可失败初始化器
    override init(name: String) {
        super.init(name: name)!
    }
}
class Person {
    var name: String
    //非可失败初始化器
    init(name: String) {
        self.name = name
    }
}

class Student : Person {
    //重写成可失败初始化器
    override init?(name: String) { //报错
        super.init(name: name)
    }
}

反初始化器

  • deinit叫做反初始化器,类似于C++中的析构函数,OC中dealloc方法;
  • 当类的实例对象被释放内存时,就会调用实例对象的 deinit方法;
  • deinit方法,不接收任何参数,不能写小括号,不能自行调用,由系统去调用;
  • 父类的deinit方法能被子类继承;
  • 子类的deinit方法调用完毕后,会去调用父类的deinit方法;
class Person {
    deinit {
        print("Person对象销毁了")
    }
}

class Student : Person {
    deinit {
        print("Student对象销毁了")
    }
}

func test() -> Void {
    var stu = Student()
}

print("-----")
test() //Student对象销毁了 -- Person对象销毁了
print("-----")

可选链

class Car {
    var price: Int = 0
}

class Dog {
    var weight: Int = 0
}

class Person {
    var name: String = ""
    var dog: Dog = Dog()
    var car: Car? = Car()
    func age() -> Int {
        18
    }
    func eat() -> Void {
        print("Person eat")
    }
    subscript(index: Int) -> Int{
        index
    }
}

var person: Person? = Person()
//如果person为空,不会调用age方法,直接返回nil
//如果person不为空,则调用age方法,返回age
var age = person?.age() //Int?
var name = person?.name //String?
var index = person?[6]  //Int?

//person?.eat()的返回值是一个可选项类型,则可以使用可选项绑定
if let _ = person?.eat(){
    print("eat 调用成功")
}else{
    print("eat 调用失败")
}

var dog = person?.dog //Dog? 可选项
var weight = person?.dog.weight //Int? 可选项
var price = person?.
  • 如果可选项为nil,那么调用方法,下标,属性都会失败,返回结果为nil;
  • 如果可选项不为nil,那么调用方法,下标,属性会成功,返回结果会被包装成可选项,如果结果本来就是可选项,那么不会再次进行包装;
  • 多个?可以链接在一起,组成可选链;
  • 如果链中的任何一个节点为nil,那么整个链就会调用失败,返回结果为nil;
var scores = [
    "Jack":[44,55,67],
    "Rose":[23,59,89]
]

var arr = scores["jack"] //[Int]? 可选项
var s = scores["jack"]?[0]

var num1: Int? = 5
num1? = 10 //Optional(10)

var num2: Int? = nil
num2? = 10 //nil

var dic: [String : (Int,Int) -> Int] = [
    "sum" : (+),
    "diff" : (-)
]

var result = dic["sum"]?(10,20) //Optional(30)
  • scores定义的是一个二维数组,scores["jack"]是取出一个一维数组,如果key乱传,那么得到的结果就是nil,所以scores["jack"]再根据下标去取元素时,需加上?,因为如果scores["jack"]为nil,就不会去取元素了,最终得到的结果是一个可选项类型;

你可能感兴趣的:(Swift5.x入门13--继承,final,初始化器,可选链)