Swift 构造方法与析构方法

//
//  StructureMethod.swift
//  SwiftCode
//
//  Created by Alisa on 2022/4/8.
//  Copyright © 2022 Alisa. All rights reserved.
//

import Foundation

/*
 
**构造方法
    swift语言要求结构体和类必须在构造方法结束前完成其中存储属性的构造,延时存储属性例外。因此,开发者在设计类时,往往采用两种方法来处理存储属性:
        <1>在类和结构体中声明存储属性时直接为其设置初始默认值
        <2>在类和结构体的构造方法中对存储属性进行构造或者设置默认值
    
    swift语言中,所有的构造方法都需要使用init()来标识,开发者可以通过函数重载来创建适合各个场景的构造方法。
    
    如果类或结构体中所有存储属性都有初始默认值,那么开发者不显示地提供任何构造方法,编译器也会默认自动生成一个无参的构造方法init().
 
    和类不同的是,结构体可以不实现其构造方法,,编译器会默认生成一个构造方法,将所有的属性作为参数
 
    如果开发者为值类型结构(例如:结构体)提供了一个自定义的构造方法,则系统默认生成的构造方法将失效,系统为了防止开发者调用自定义的构造方法时误调用到系统
 生成的构造方法,使构造方法可以嵌套使用
 
    指定构造方法:指定构造方法的官方名称:Designated,指定构造方法是类的基础构造方法,任何类都至少有一个指定构造方法,指定构造方法声明时不需要任何关键字
 修饰,
            
    便利构造方法:便利构造方法的官方名称:Convernience,是为了开发者方便使用,为类额外添加的构造方法
               注意:便利构造方法最后也是要调用指定构造方法的
    
    指定构造方法与便利构造方法的使用原则:
        子类的指定构造方法中必须调用父类的指定构造方法
        便利构造方法中必须调用当前类的其它构造方法
        便利构造方法归根结底要调用某个指定构造方法
 
    构造方法的继承关系,以及使用原则:
        <1>在继承关系中,如果子类没有覆写或者重写任何指定构造方法,则默认子类会继承父类所有的指定构造方法
        <2>如果子类中提供了父类所有的指定构造方法(无论是通过继承方式还是覆写方式),则子类会默认继承父类的便利构造方法
        <3>覆写父类的指定构造方法需要使用override关键字,和普通方法的覆写一样
        <4>便利构造方法并不存在覆写的概念,便利构造方法必须调用类本身的其它构造方法,因此无论子类中定义的便利构造方法和父类是否相同,其都是子类独立的构造
            方法
        <5>子类覆写了父类的便利构造方法,那么需要子类覆写这个构造方法依赖的父类相关的指定构造方法
 
    构造方法的安全性检查:
        <1>子类的指定构造方法中,必须完成当前类所有存储属性的构造,才能调用父类的指定构造方法
            目的:在构造完从父类继承下来的所有存储属性前,本身定义的所有存储属性也已构造完成
        <2>子类如果要自定义父类中存储属性的值,必须在调用父类的构造方法之后进行设置
            目的:子类在设置从父类继承下来的存储属性时,此属性已构造完成
        <3>如果便利构造方法中需要重新设置某些存储属性的值,必须在调用指定构造方法之后进行设置
            目的:便利构造方法中对存储属性的设置不会被指定构造方法中的设置覆盖
        <4>子类在调用父类构造方法之前,不能使用self来引用属性
            目的:在使用self关键字调用实例本身时,实例已经构造完成
    
    可失败的构造方法:一个构造方法可能需要一些特定情况的参数,当传递的参数不符合要求时,开发者需要让这次构造失败。这时使用可失败的构造方法。
                    可失败构造方法的定义非常简单,只需要使用init?()即可,在实现可失败构造方法时,开发者可以根据需求返回nil
    
    必要构造方法:如果一个类中的某些构造方法被指定为必要构造方法,则其子类必须实现这个构造方法(可以通过继承和覆写的方式),必要构造方法需要使用required关键字进行修饰
 
**析构方法
    析构方法:析构方法和构造方法是互逆的,当实例被销毁时,系统会调用它的析构方法
            我们可以在析构方法中销毁实例使用的内存资源
 

**知识拓展
    属性的设计技巧:
        如果一个类或结构体大多数实例的某个属性都需要相同的值,开发者应该将其设置为这个属性的初始默认值
        如果某个属性在逻辑上是允许为nil的,开发者可以将其声明成Optional类型,对于Optional类型的属性,如果在构造方法中不进行赋值,则会默认赋值为nil

    属性监听器的调用时机:
        在对存储属性设置默认值或者在构造方法中对其构造时,并不会触发属性监听器,只有在构造完成后,再对其赋值时才会触发
 
*/


class Music{
    
    var singer:String
    let time:Double
    var zone:String = "China"{
        didSet{
            print("current zone \(self.zone)")
        }
    }
    var musicType:String = "popular"{
        didSet{
            print("current musicType \(self.musicType)")
        }
    }
    
    //init()、init(singer:String, time:Double, zone:String)都是指定构造方法,一个类可以设置多个指定构造方法
    init() {
        self.singer = "Nobody"
        self.time = 0.0
        self.zone = "NoWhere"
        print("Designated Empty Method")
    }
    init(singer:String, time:Double, zone:String) {
        self.time = time
        self.singer = singer
        self.zone = zone
        print("Designated Method")
    }
    
    //下面这两个是便利构造方法,一个类也可以有多个便利构造方法
    convenience init(singer:String, time:Double, zone:String, turnType:String){
        self.init(singer: singer, time: time, zone: zone)
        print("This music is turn on")
    }
    convenience init(musicType:String){
        self.init()
        print("This music type is \(musicType)")
    }
}

class RapMusic:Music{
    
    var speed:Int = 0
    var key:Int = 0
    //子类覆写父类的指定构造方法,在子类中必须调用必须调用父类的指定构造方法
    override init(){
        super.init(singer: "Piter", time: 2.34, zone: "America")
    }
    convenience init(speed:Int) {
        self.init()
        self.speed = speed
        print("init rap music speed is \(self.speed)")
    }
    convenience init(key:Int) {
        self.init()
        self.key = key
    }
}

class PopularMusic:Music{
    
    var speed:Int = 0
    var popularZone:String
    lazy var popularYear:Int = 0
    
    override init() {
        //必须完成当前类所有存储属性的构造
        self.popularZone = "Beijing"
        //子类在调用父类构造方法之前,不能使用self来引用属性
        //self.singer = "Anny"    //'self' used in property access 'singer' before 'super.init' call
        
        super.init()    //Property 'self.popularZone' not initialized at super.init call,需要先给popularZone赋初值
        print("PopularMusic Designated Empty Method")
        
        self.singer = "Anny"
        print("PopularMusic singer is:\(self.singer)")
    }
    
    //Initializer does not override a designated initializer from its superclass
    //子类覆写了父类的便利构造方法,那么需要子类覆写这个构造方法依赖的父类相关的指定构造方法
    convenience init(musicType:String) {
        //self.singer = "Anna"    //'self' used before 'self.init' call or assignment to 'self',需要在调用完指定构造方法之后进行
        self.init()
        print("This popular music type is \(musicType)")
        
        //如果便利构造方法中需要重新设置某些存储属性的值,必须在调用指定构造方法之后进行设置
        self.singer = "Anna"
        print("PopularMusic singer is:\(self.singer) in convenience init!")
    }
}

//可失败构造方法与必要构造方法
class Check{
    var property:Int
    
    //必要构造方法
    required init(param:Int){
        property = param
    }
    
    //可失败的构造方法
    init?(param:Bool){
        //使用守护语句,当param为ture时才进行构造
        guard param else{
            return nil
        }
        property = 1
    }
    
    //属性的构造可以使用闭包的方式
    var name:Int = {
        print("Check name init value")
        return 6+6
    }()     //这个()不能丢掉,否则这个属性就变成了一个只读的计算属性了,发生了本质的区别
    
    //析构方法
    deinit{
        print("Check 实例被销毁")
    }
}
class CashCheck:Check{
    
    //这里不用加override,会报一个警告:'override' is implied when overriding a required initializer
    required init(param: Int) {
        super.init(param: param)
        print("This is CashCheck override required init")
        
        property = param + 1   //在父类的init方法调用后再进行新的赋值
    }
}

class StructureMethod{
    
    //简单的使用指定构造方法与便利构造方法
    func useDesignatedAndConvernience(){
        let musicOne = Music(singer: "pany", time: 3.20, zone: "Ace", turnType: "turn on")
        musicOne.musicType = "popular"
        
        let musicTwo = Music(musicType: "Rap")
        musicTwo.zone = "China"
    }
    
    //构造方法的继承关系
    func useInheritanceRelationship(){
        let popularM = PopularMusic(musicType: "current popular")
        /* 构造方法的调用顺序如下:
         Designated Empty Method    父类的指定构造方法
         PopularMusic Designated Empty Method   子类的指定构造方法
         This popular music type is current popular     子类自己的便利构造方法
         */
        popularM.speed = 2
    }
    
    //构造方法的安全性检查
    func useSafetyInspection(){
        let popularM = PopularMusic(musicType: "current popular use safety")
        /* 构造方法的调用顺序:
         Designated Empty Method
         PopularMusic Designated Empty Method
         PopularMusic singer is:Anny
         This popular music type is current popular use safety
         PopularMusic singer is:Anna in convenience init!
         */
        popularM.popularYear = 2022
    }
    
    //可失败构造方法与必要构造方法
    func useCanFail(){
        
        //使用可失败的构造方法
        let check = Check(param: false)
        //初始化中,这个被打印了:Check name init value
        if(check == nil){
            print("Check 初始化失败!")
        }
        
        let cashCheck = CashCheck(param: 2)
        /*打印如下:
         Check name init value  父类计算属性在初始化时,闭包的打印
         This is CashCheck override required init   子类覆写的父类必要构造方法
         */
        print("cashCheck is: \(cashCheck.property), name is: \(cashCheck.name)")
        //cashCheck is: 3, name is: 12
        
        //这个方法结束前,系统释放check对象时,调用了Check类的析构方法,Check 实例被销毁
        //注意:这里只打印了一次:Check 实例被销毁,说明子类的实例变量在被销毁时,没有调用父类的析构方法
    }
    
    //析构方法的使用
    func useDeinit(){
        
        //注意这里初始化是必须是可选类型的变量,且必须为var的变量
        var check:Check? = Check(param: 22) //否则系统会报错:'nil' cannot be assigned to type 'Check'
        check = nil
        /* 打印顺序如下:
         Check name init value  //初始化时name计算属性闭包中赋值
         Check 实例被销毁    实例被置为nil时调用析构方法
         */
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值