19.Swift学习之构造函数与析构函数

简介: 重要说明本文中提到的构造函数,在很多书中有其他的说法,如构造器,构造方法,初始化,初始函数等本文中提到的析构函数,在很多书中有其他的说法,如反构造器,析构方法,反初始化,反初始函数等构造函数的介绍构造函数用于初始化一个类的实例(创建对象)...

重要说明

  • 本文中提到的构造函数,在很多书中有其他的说法,如构造器,构造方法,初始化,初始函数等
  • 本文中提到的析构函数,在很多书中有其他的说法,如反构造器,析构方法,反初始化,反初始函数等

构造函数的介绍

  • 构造函数用于初始化一个类的实例(创建对象)
  • 默认情况下载创建一个类时,必然会调用一个构造函数
  • 即便是没有编写任何构造函数,编译器也会提供一个默认的构造函数
  • 如果是继承自NSObject,可以对父类的构造函数进行重写

默认构造函数

  • 在创建类和结构体的实例时必须为所有的存储属性设置一个合适的初始值,如果不是在定义时初始化值,可以在构造函数中赋值
  • 构造函数就像一个没有形式参数的实例方法,使用 init 关键字来写
class Person {
    var name:String
    var age:Int
    var sex:String
    //1.构造函数没有func修饰
    //2.构造函数默认完成调用 不能手动调用
    init() {      
        print("被调用")
        name = "Zhangsan"
        age = 10
        sex = "male"
    }
}
var p = Person()
p.age
p.name
p.sex
自定义构造函数
  • 希望在创建一个对象时手动给属性赋值(属性的值是在外面传进去的)
  • 可以自定义构造函数
  • 自定义构造函数和默认构造函数可以同时存在
//定义类
class Person {
    var name:String
    var age:Int
    var sex:String
    //默认构造函数
    init() {

        print("被调用")
        name = "Zhangsan"
        age = 10
        sex = "male"
    } 
    //自定义构造函数
    init(name:String, age:Int, sex:String) {        
        self.name = name
        self.age = age
        self.sex = sex   
    }
}
var p = Person()
p.age
p.name
p.sex

var p2 = Person(name: "Wangli", age: 11, sex: "female")
p2.age
p2.name
p2.sex

结构体类型的成员构造函数

  • 如果结构体类型中没有定义任何自定义构造函数,它会自动获得一个成员构造函数
//定义了一个名为 Size 有两个属性分别是 width 和 height 的结构体,这两个属性通过分配默认值 0.0 ,从而被推断为 Double 类型
struct Size {
    var width = 0.0, height = 0.0
}
//Size 结构体自动接收一个 init(width:heght:) 构造函数
let twoByTwo = Size(width: 2.0, height: 2.0)

值类型的构造函数委托

  • 构造函数可以调用其他构造函数来执行部分实例的初始化。这个过程,就是所谓的构造函数委托
  • 构造函数委托对于值类型和类类型是不同的。
  • 值类型(结构体和枚举)不支持继承,所以它们的构造函数委托的过程相对简单
  • 注意如果为值类型定义了自定义构造函数,就不能访问默认构造函数或者是成员构造函数
struct Size {
    var width = 0.0, height = 0.0
    init() {    
        //构造函数委托
        self.init(width: 2.0, height: 2.0)
    }
    init(width:Double, height:Double) {
        self.width = width
        self.height = height
    }
}
//要么不要写任何构造函数,要么全写所有的构造函数,否则下面第二种调用方式会有问题,参考上面第四条
var size = Size()
size.width
size.height
var size2 = Size(width: 1.2, height: 1.2)
size2.width
size2.height

类的继承和初始化

  • 所有类的存储属性——包括从它的父类继承的所有属性都必须在初始化期间分配初始值。
  • Swift 为类类型定义了两种构造函数以确保所有的存储属性接收一个初始值,它们就是指定构造函数(Designated Initializer)和便捷构造函数(Convenience Initializer)
    • 指定构造函数是类的主要构造函数。指定构造函数可以初始化所有类引用的属性并且调用合适的父类构造函数来继续这个初始化过程给父类链
    • 一个类通常只有一个指定构造函数并且每个类至少得有一个指定构造函数
    • 便捷构造函数是次要的,可以在相同的类里定义一个便捷构造函数来调用一个指定构造函数给指定构造函数设置默认形式参数
//类的指定构造函数
init(parameters) {
    statements
}
//便捷构造函数有着相同的书写方式,但是要用 convenience 修饰符放到 init 关键字前,用空格隔开
convenience init(parameters) {
    statements
}

类类型的构造函数委托

  • 为了简化指定和便捷构造函数之间的调用关系,Swift 在构造函数之间的委托调用有下面的三个规则:

    • 规则 1——指定构造函数必须从它的直系父类调用指定构造函数
    • 规则 2——便捷构造函数必须从相同的类里调用另一个构造函数(可以是指定也可以是便捷)
    • 规则 3——便捷构造函数最终必须调用一个指定构造函数
  • 简单记忆的这些规则的方法如下:

    • 指定构造函数必须总是向上委托。
    • 便捷构造函数必须总是横向委托。


      img_42acec99d3f067f07943f12660661da5.png
      类类型的构造函数委托
class Car{
    var speed:Double    
    //Designated Initializer
    init(speed:Double) {
        self.speed = speed
    }
    convenience init(){
        self.init(speed: 60.0)
    }
}

class Bus : Car {    
    var wheels : Int 
    init(wheels: Int) {      
        self.wheels = wheels   
        //由于子类继承了父类中的存储属性 所以必须借助父类的指定构造函数来初始化继承的那个存储属性的值
        //一定要在子类的属性初始化完毕以后调用
        super.init(speed: 120.0)
    }
    convenience init(){    
        self.init(wheels: 6)
    }
    
}

构造函数的继承与重写

  • 在Swift中,子类的构造函数有两种来源,首先是自己拥有的构造函数,其次是从父类中继承过来的构造函数。但是,并不是所有父类构造函数都能够被子类继承。子类继承父类的构造函数是有条件的,遵守以下2个规则:
    • 规则1——如果子类没有定义任何指定构造函数,它会自动继承父类所有指定构造函数
    • 规则2——如果子类提供了所有父类指定构造函数的实现(通过规则1继承来的或者提供自定义实现的),那么它会自动继承所有父类便捷初始化器
  • 如果一个子类中任意的构造器和父类的便利构造器一模一样, 不算重写
class Person {
    var name: String!
    var weight: Double
    // 普通自定义构造函数
    init(name: String) {
        self.name = name
        self.weight = 0.0
    }
    // 定义指定构造函数
    init(name: String, weight: Double) {
        self.name = name
        self.weight = weight
    }
    
    // 定义便利构造函数
    convenience init(n name: String, w weight: Double) {
        // 便利构造函数必须调用同类中的指定构造函数
        self.init(name: name, weight: weight)
    }
    
    convenience init(showStr: String) {
        self.init(name: "", weight: 0.0)
        print(showStr)
    }
}

class Man: Person {
    var sex: String = "男"
    override init(name: String) {
        super.init(name: name)
        self.name = name
        self.weight = 0.0
    }
    override init(name: String, weight: Double) {
        
        self.sex = "女"
        // 子类的指定构造函数中必须调用父类的构造函数
        // 重写的时候,必须将调用父类的构造函数语句放在调用父类属性的前面
        super.init(name: name, weight: weight)
        self.name = name
        self.weight = weight
    }
    // 定义构造函数与父类的便利构造函数一样, 这里不算重写
    convenience init(showStr: String) {
        self.init(name: "", weight: 0.0)
        print(showStr)
    }
}

var manA = Man(name: "ZhangSan", weight: 62.0)
var manB = Man(showStr: "Hello Swift")

可失败的构造函数

  • 定义类、结构体或枚举初始化时可以失败
  • 失败可能由以下几种方式触发,包括给初始化传入无效的形式参数值,或缺少某种外部所需的资源,又或是其他阻止初始化的情况
  • 为了处理这种可能,在类、结构体或枚举中定义一个或多个可失败的构造函数。通过在 init 关键字后面添加问号init?
  • 可失败的构造函数里面应该有一个 return nil 的语句(虽然没有也不报错)
  • 通过可失败的构造函数构造出来的实例是一个可选型
struct Animal {
    let species: String
    init?(species: String) {
        //  返回一个nil
        if species.isEmpty { return nil }
        self.species = species
    }
}
//返回的类型是当前类型的可选型
let cat = Animal(species: "CAT")
if let cat = cat {   
    cat.species
}
let dog = Animal(species: "")
if let dog = dog {   
    dog.species   
}

必要构造函数

  • 在类的构造函数前添加required 修饰符来表明表明它是一个必要构造函数
  • 当子类重写父类的必要构造函数时,必须在子类的构造函数前也要添加 required 修饰符以确保当其它类继承该子类时,该构造函数同为必要构造函数
  • 在重写父类的必要构造函数时,不需要添加 override 修饰符
class SomeClass {
    required init() {
    }
}
class SomeSubclass: SomeClass {
    required init() {
    }
}

析构函数

  • Swift 会自动释放不再需要的实例以释放资源
    • Swift 通过自动引用计数(ARC)处理实例的内存管理
    • 当引用计数为0时,系统会自动调用析构函数(不可以手动调用)
    • 通常在析构函数中释放一些资源(如移除通知等操作)
  • 析构函数的写法
//后面连()都没有
deinit {
    // 执行析构过程
}
示例练习
class Person {
    var name : String
    var age : Int

    init(name : String, age : Int) {
        print("Person-init")
        self.name = name
        self.age = age
    }

    deinit {
        print("Person-deinit")
    }
}

var person : Person? = Person(name: "Zhangsan", age: 18)
person = nil
目录
相关文章
|
6月前
|
人工智能 物联网 API
LLM 大模型学习必知必会系列(十三):基于SWIFT的VLLM推理加速与部署实战
LLM 大模型学习必知必会系列(十三):基于SWIFT的VLLM推理加速与部署实战
LLM 大模型学习必知必会系列(十三):基于SWIFT的VLLM推理加速与部署实战
|
API Swift
Swift实用小册13:Initializer构造函数的使用
构造函数是一种特殊的函数,主要用来在创建对象时初始化对象,为对象成员变量设置初始值。 在之前的章节中,我们在类、结构体中可以声明常量和变量,并给变量/常量赋予了初始值,但如果不同业务当中存在初始值不同的情况,按照之前的方式,我们需要在具体业务中重新赋值。
188 0
Swift实用小册13:Initializer构造函数的使用
|
Swift
(一)swift的基本学习
(一)swift的基本学习
175 0
|
开发工具 Swift iOS开发
Swift学习第二练——Swift项目时光电影
Swift学习第二练——Swift项目时光电影
161 0
Swift学习第二练——Swift项目时光电影
|
Swift 开发者 iOS开发
Swift学习第一练——用Swift实现的FlappyBird小游戏
Swift学习第一练——用Swift实现的FlappyBird小游戏
191 0
Swift学习第一练——用Swift实现的FlappyBird小游戏
|
JavaScript Java iOS开发
1.Swift学习之介绍
简介 Swift 语言由苹果公司在 2014 年推出,用来撰写 Mac OS 和 iOS 应用程序 Apple WWDC 2014 横空出世 Swift 历史 2010 年 7 月,苹果开发者工具部门总监 Chris Lattner...
1179 0
|
Swift C语言
2.Swift学习之初探
Playground是什么? 从Xcode6开始出现(Swift开始出现) 翻译为:操场/游乐场 对于学习Swift基本语法非常方便 所见即所得(快速查看结果) 语法特性发生改变时,可以快速查看 Playground Swift最...
855 0
|
iOS开发
Swift学习第十三枪-使用Swift开发IOS中蓝牙4.0的开发流程
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.
1060 0
|
数据库 iOS开发 数据库管理
Swift学习第十二枪----移动数据库王者Realm入门教程
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010046908/article/details/51636392 今天给大家带来的是堪称是一个可以替代SQLite,Core Data 的以及ORMlibraries的轻量级数据库—Realm移动端数据库。
952 0
|
JSON 数据格式 iOS开发
Swift学习第十枪-AFNetwoking3.0的封装
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010046908/article/details/51588829 下面是我的新建的Swift学习交流群,欢迎大家一起来共同学习Swift。
829 0